Article SEO SEO Technique

Balises Open Graph OG Tags

On-Page SEO : Balises Open Graph, éléments HTML essentiels

Introduction aux balises Open Graph en On-Page SEO

Dans un monde numérique où les réseaux sociaux dominent le trafic web, les balises Open Graph, ou OG Tags, représentent un pilier incontournable de l'On-Page SEO. Ces éléments HTML meta, intégrés dans la section head de vos pages, contrôlent précisément l'apparence de vos liens lorsqu'ils sont partagés sur Facebook, Twitter, LinkedIn ou Instagram. Imaginez : un utilisateur partage votre article, et au lieu d'un aperçu générique et fade, s'affiche un titre accrocheur, une description persuasive et une image percutante de 1200x630 pixels. Résultat ? Un taux de clic boosté et un trafic organique en hausse.

Pourquoi ces balises Open Graph SEO sont-elles si cruciales en 2025 ? Les résultats organiques captent 94 % des clics internet, avec un CTR du premier résultat atteignant 39,8 % – et jusqu'à 42,9 % avec un featured snippet. Dans ce contexte, où Google représente 89,79 % de part de marché mondial et génère 84,5 milliards de visites en janvier 2025 seul, optimiser le partage social devient une arme stratégique. Les OG Tags ne influencent pas directement le classement Google, mais ils amplifient indirectement votre visibilité en générant des partages viraux, des backlinks naturels et un engagement accru. Sans elles, 25 % des problèmes de partage social persistent, privant votre site de précieuses opportunités.

Cet article complet sur les balises Open Graph élément HTML vous guide pas à pas : définition, rôle en SEO, bonnes pratiques avancées, outils de validation et tendances 2025 comme l'intégration avec les AI Overviews. Que vous soyez débutant ou expert en référencement naturel, découvrez comment implémenter ces meta tags pour transformer chaque partage en lead qualifié. Prêt à booster votre On-Page SEO ? Plongeons dans les détails techniques et actionnables.

Concepts clés des balises Open Graph en SEO

Les balises Open Graph, développées initialement par Facebook en 2010, forment un protocole standardisé pour enrichir les métadonnées HTML. Prefixées par "property='og:'", elles s'insèrent dans le de votre page et dictent le rendu social des liens partagés.

Qu'est-ce qu'une balise Open Graph ? Définition technique

Une balise Open Graph est un élément HTML du type . Les principales incluent og:title pour le titre affiché (idéalement 60 caractères max), og:description pour un résumé engageant (jusqu'à 200 caractères), og:image pour l'image principale (JPEG/PNG, 1200x630 px recommandé, avec og:image:width et og:image:height pour précision), og:url pour l'URL canonique, et og:type pour le type de contenu (article, website, video.movie, etc.). Exemple complet :






Ces balises assurent une compatibilité HTTPS via og:image:secure_url et évitent les duplications URL grâce à og:url.

Rôle des OG Tags dans l'On-Page SEO et le trafic organique

Indirectement liées au SEO, les OG Tags améliorent le CTR social, qui alimente le trafic organique représentant 94 % des clics. Le premier résultat organique capte 39,8 % des clics, mais un partage optimisé peut multiplier les visites par 3, comme pour les contenus longs supérieurs à 3 000 mots, qui génèrent 3 fois plus de trafic, 4 fois plus de partages et 3,5 fois plus de backlinks que ceux de 1 400 mots. En 2025, avec l'essor des AI Overviews (de 6,5 % à 13,1 % des SERP), des OG Tags bien structurés facilitent l'intégration multimédia (og:video, og:audio), renforçant la visibilité face aux mots-clés ultra-compétitifs (CTR de 0,0008 % pour >100 000 recherches/mois).

Impact des balises Open Graph sur le partage social

Sur Facebook, un lien sans OG Tags reprend maladroitement le et une image par défaut, causant 25 % des échecs de partage. Avec elles, l'aperçu devient pro : titre personnalisé répondant à l'intention de recherche, description teaser incluant mots-clés, image attractive. Résultat : augmentation de 20-30 % de la visibilité sociale, plus d'engagements et de backlinks naturels, boostant le SEO global.</p> <h2 id="bonnes-pratiques-pour-optimiser-les-og-tags-open-graph-seo">Bonnes pratiques pour optimiser les OG Tags Open Graph SEO</h2> <p>Maîtriser les balises Open Graph nécessite une approche structurée. Voici des conseils actionnables pour un On-Page SEO irréprochable.</p> <h3 id="optimiser-le-contenu-des-balises-open-graph">Optimiser le contenu des balises Open Graph</h3> <ul> <li><strong>Titre attractif (og:title)</strong> : Concis (60 caractères), incluez le mot-clé principal et une variante d'intention de recherche. Exemple : "Balises Open Graph SEO : Guide 2025" au lieu de copier le title HTML.</li> <li><strong>Description informative (og:description)</strong> : 150-200 caractères, persuasive, avec mots-clés secondaires. Évitez le keyword stuffing ; priorisez l'appel au clic.</li> <li><strong>Image optimisée (og:image)</strong> : 1200x630 px minimum, < 8 Mo, pertinente. Ajoutez og:image:alt pour accessibilité et SEO image.</li> <li><strong>Type et site (og:type, og:site_name)</strong> : "article" pour blogs, "website" pour homepages ; nommez votre marque pour reconnaissance.</li> </ul> <h3 id="ameliorer-la-structure-html-des-og-tags">Améliorer la structure HTML des OG Tags</h3> <ul> <li>Incluez toutes les balises essentielles sur chaque page : personnalisez-les, évitez les génériques.</li> <li>Utilisez og:url canonique pour gérer les redirections 301 et paramètres UTM.</li> <li>Intégrez des propriétés avancées : og:video pour multimédia, og:audio pour podcasts, compatibles AI Overviews 2025.</li> <li>Testez avec Facebook Sharing Debugger : rafraîchit le cache et détecte erreurs (manque image : 25 % problèmes).</li> </ul> <h3 id="creer-du-contenu-de-qualite-pour-maximiser-les-partages">Créer du contenu de qualité pour maximiser les partages</h3> <p>Les OG Tags brillent avec un contenu premium. Visez >3 000 mots pour 3x trafic organique. Produisez des articles éducatifs, listicles ou how-to émotionnels. Exemple : un guide OG Tags comme celui-ci génère partages viraux. Mesurez l'engagement via Google Analytics : pages avec haut CTR social rankent mieux.</p> <h3 id="utiliser-des-images-pertinentes-et-techniques-avancees">Utiliser des images pertinentes et techniques avancées</h3> <p>Choisissez des images brandées, testées A/B. Spécifiez og:image:width=1200, og:image:height=630. Pour HTTPS, og:image:secure_url. Évitez une seule image par site ; personnalisez par page pour pertinence.</p> <h3 id="mettre-a-jour-regulierement-vos-balises-open-graph">Mettre à jour régulièrement vos balises Open Graph</h3> <p>Surveillez via Google Search Console (erreurs crawl) et Analytics (trafic social). Adaptez aux tendances 2025 : compatibilité X/Twitter (twitter:card), LinkedIn. Marché SEO mondial à 146,96 milliards $ : investissez dans audits trimestriels.</p> <h2 id="outils-et-ressources-pour-valider-les-balises-open-graph">Outils et ressources pour valider les balises Open Graph</h2> <p>Implémentez sans effort avec ces outils pros.</p> <table border="1" cellpadding="10" cellspacing="0" style="width:100%; border-collapse: collapse;"> <thead> <tr> <th>Outil</th> <th>Fonction</th> <th>Avantages pour On-Page SEO</th> </tr> </thead> <tbody> <tr> <td>Facebook Sharing Debugger</td> <td>Teste aperçu liens</td> <td>Détecte erreurs, rafraîchit cache</td> </tr> <tr> <td>Google Search Console</td> <td>Surveille meta tags</td> <td>Signale manques OG, CTR organique</td> </tr> <tr> <td>Google Analytics</td> <td>Trafic social</td> <td>Mesure ROI OG Tags (94% clics organiques)</td> </tr> <tr> <td>Yoast SEO / Rank Math (WP)</td> <td>Génération auto</td> <td>Intègre OG sans code</td> </tr> <tr> <td>Webflow Page Settings</td> <td>Config OG native</td> <td>Optimisé 2025 AI Overviews</td> </tr> </tbody> </table> <p>Pour Webflow, accédez à Page Settings > Open Graph Settings. Plugins CMS automatisent 80 % du travail.</p> <h2 id="erreurs-courantes-a-eviter-avec-les-og-tags-seo">Erreurs courantes à éviter avec les OG Tags SEO</h2> <p>25 % des sites souffrent de meta manquantes. Évitez : copier title sans personnalisation (perdez intentions recherche), images trop petites (<1200px), descriptions spammées. Vérifiez mobile : aperçus responsive essentiels en 2025.</p> <h2 id="tendances-2025-og-tags-et-ia-en-on-page-seo">Tendances 2025 : OG Tags et IA en On-Page SEO</h2> <p>AI Overviews (13,1 % SERP mars 2025) favorisent contenus multimédias : og:video booste visibilité. Intégrez Twitter Cards pour X. Marché SEO explose à 146,96 milliards $ : priorisez OG pour social signals indirects.</p> <h2 id="faq-balises-open-graph-element-html">FAQ balises Open Graph élément HTML</h2> <p><strong>Les OG Tags sont-elles nécessaires pour tous les sites ?</strong> Oui, surtout e-commerce et blogs : 94 % trafic organique via clics optimisés.</p> <p><strong>Impactent-elles le classement Google ?</strong> Indirectement via CTR social et backlinks (3,5x plus pour contenus longs).</p> <p><strong>Différence avec meta tags classiques ?</strong> Meta pour SERP (title, description) ; OG pour social sharing.</p> <p><strong>Suffisent-elles seules en On-Page SEO ?</strong> Non, combinez avec H1-H6, schema.org, vitesse site.</p> <h2 id="conclusion-implementez-vos-og-tags-des-aujourd-hui">Conclusion : Implémentez vos OG Tags dès aujourd'hui</h2> <p>Les balises Open Graph transforment vos partages en machines à trafic. Avec 84,5 milliards visites Google/mois, ne négligez pas le social. Auditez votre site, testez via Debugger, mesurez via Analytics. Boostez votre On-Page SEO OG Tags maintenant pour dominer 2025 !</p> </body> </html> </div> <!-- CTA Section --> <div class="mt-12 pt-8 border-t border-gray-200"> <div class="bg-gradient-to-r from-purple-600 to-blue-600 rounded-xl p-8 text-center text-white"> <h3 class="text-2xl font-bold mb-4">Besoin d'aide avec votre SEO ?</h3> <p class="mb-6 text-purple-100">Notre équipe d'experts peut vous aider à optimiser votre site e-commerce</p> <div class="flex flex-col sm:flex-row gap-4 justify-center"> <a href="/seo-ecommerce" class="bg-white text-purple-600 px-8 py-3 rounded-lg font-semibold hover:bg-purple-50 transition inline-block"> Découvrir nos services SEO </a> <a href="/#contact" class="bg-purple-800 text-white px-8 py-3 rounded-lg font-semibold hover:bg-purple-900 transition inline-block"> Nous contacter </a> </div> </div> </div> </article> <!-- Section Commentaires --> <div class="mt-12 max-w-4xl mx-auto bg-white rounded-2xl shadow-xl p-8 md:p-12"> <h2 class="text-3xl font-bold text-gray-900 mb-6 flex items-center gap-3"> <i class="fas fa-comments text-purple-600"></i> Commentaires </h2> <!-- Liste des commentaires approuvés --> <div id="comments-list" class="mb-8 space-y-6"> <!-- Les commentaires approuvés seront chargés ici via JavaScript --> </div> <!-- Formulaire de commentaire --> <div class="border-t border-gray-200 pt-8"> <h3 class="text-xl font-semibold text-gray-900 mb-4">Laisser un commentaire</h3> <form id="comment-form" class="space-y-4"> <input type="hidden" id="article-slug" value="balises-open-graph-og-tags"> <div class="grid md:grid-cols-2 gap-4"> <div> <label for="comment-name" class="block text-sm font-medium text-gray-700 mb-2">Nom *</label> <input type="text" id="comment-name" name="name" required class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-600 focus:border-transparent"> </div> <div> <label for="comment-email" class="block text-sm font-medium text-gray-700 mb-2">Email *</label> <input type="email" id="comment-email" name="email" required class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-600 focus:border-transparent"> </div> </div> <div> <label for="comment-message" class="block text-sm font-medium text-gray-700 mb-2">Message *</label> <textarea id="comment-message" name="message" rows="5" required class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-600 focus:border-transparent"></textarea> </div> <div class="text-sm text-gray-600"> <i class="fas fa-info-circle text-purple-600"></i> Votre commentaire sera soumis à modération avant publication. </div> <button type="submit" class="bg-gradient-to-r from-purple-600 to-blue-600 text-white px-8 py-3 rounded-lg font-semibold hover:from-purple-700 hover:to-blue-700 transition inline-flex items-center gap-2"> <i class="fas fa-paper-plane"></i> Publier le commentaire </button> </form> <div id="comment-status" class="mt-4 hidden"></div> </div> </div> <!-- Navigation Articles --> <div class="mt-12 max-w-4xl mx-auto"> <a href="/blog" class="inline-flex items-center gap-2 text-purple-600 hover:text-purple-800 transition font-semibold"> <i class="fas fa-arrow-left"></i> Retour au blog </a> </div> </div> </section> <!-- Script pour les commentaires --> <script> const articleSlug = 'balises-open-graph-og-tags'; // Charger les commentaires approuvés async function loadComments() { try { const response = await fetch(`/gestion-commentaires/get-comments.php?slug=${articleSlug}`); const data = await response.json(); const commentsList = document.getElementById('comments-list'); if (data.success && data.comments.length > 0) { commentsList.innerHTML = data.comments.map(comment => ` <div class="border-l-4 border-purple-600 pl-4 py-2"> <div class="flex items-center gap-2 mb-2"> <strong class="text-gray-900">${comment.name}</strong> <span class="text-sm text-gray-500">•</span> <span class="text-sm text-gray-500">${new Date(comment.date).toLocaleDateString('fr-FR')}</span> </div> <p class="text-gray-700">${comment.message}</p> </div> `).join(''); } else { commentsList.innerHTML = '<p class="text-gray-500 italic">Aucun commentaire pour le moment. Soyez le premier à commenter !</p>'; } } catch (error) { console.error('Erreur lors du chargement des commentaires:', error); } } // Gérer la soumission du formulaire document.getElementById('comment-form').addEventListener('submit', async function(e) { e.preventDefault(); const formData = { slug: articleSlug, name: document.getElementById('comment-name').value, email: document.getElementById('comment-email').value, message: document.getElementById('comment-message').value }; const submitBtn = this.querySelector('button[type="submit"]'); const originalText = submitBtn.innerHTML; submitBtn.disabled = true; submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Envoi en cours...'; try { const response = await fetch('/gestion-commentaires/submit-comment.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }); const result = await response.json(); const messageDiv = document.getElementById('comment-status'); if (result.success) { messageDiv.className = 'mt-4 p-4 bg-green-100 border border-green-400 text-green-700 rounded-lg'; messageDiv.textContent = 'Merci ! Votre commentaire a été soumis et sera publié après modération.'; messageDiv.classList.remove('hidden'); this.reset(); } else { messageDiv.className = 'mt-4 p-4 bg-red-100 border border-red-400 text-red-700 rounded-lg'; messageDiv.textContent = result.message || 'Une erreur est survenue. Veuillez réessayer.'; messageDiv.classList.remove('hidden'); } } catch (error) { const messageDiv = document.getElementById('comment-status'); messageDiv.className = 'mt-4 p-4 bg-red-100 border border-red-400 text-red-700 rounded-lg'; messageDiv.textContent = 'Erreur de connexion. Veuillez réessayer plus tard.'; messageDiv.classList.remove('hidden'); } finally { submitBtn.disabled = false; submitBtn.innerHTML = originalText; } }); // Charger les commentaires au chargement de la page loadComments(); </script> <footer class="bg-gray-950 text-gray-400 py-12"> <div class="container mx-auto px-6"> <div class="flex flex-col md:flex-row justify-between items-center"> <div class="mb-6 md:mb-0 flex items-start gap-4"> <img src="/images/logo.png" alt="Logo VRAIVEX" class="h-32 w-32 md:h-40 md:w-40 object-contain"> <div> <a href="#" class="text-2xl font-bold block"> <span class="gradient-text">VRAIVEX</span> </a> <p class="mt-2 text-sm">Automatisation, IA et SEO au service de la performance e-commerce</p> </div> </div> <div class="flex flex-col items-center md:items-end"> <div class="grid grid-cols-2 md:flex md:flex-wrap gap-3 md:space-x-6 mb-4 text-center md:text-right"> <a href="/#about" class="hover:text-white transition text-sm">À propos</a> <a href="/#services" class="hover:text-white transition text-sm">Services</a> <a href="/#prestations" class="hover:text-white transition text-sm">Prestations</a> <a href="/#bestsellers" class="hover:text-white transition text-sm">Best Sellers</a> <a href="/#realisations" class="hover:text-white transition text-sm">Réalisations</a> <a href="/#brands" class="hover:text-white transition text-sm">Nos Sites</a> <a href="/creation-site-ecommerce" class="hover:text-white transition text-sm">Création Sites</a> <a href="/seo-ecommerce" class="hover:text-white transition text-sm">SEO E-commerce</a> <a href="/partenaires" class="hover:text-white transition text-sm">Partenaires</a> <a href="/#contact" class="hover:text-white transition text-sm">Contact</a> </div> <p class="text-sm text-center md:text-right">© 2026 VRAIVEX. Tous droits réservés.</p> </div> </div> </div> </footer> <!-- Back to Top Button --> <button id="backToTop" class="fixed bottom-8 right-8 bg-gradient-to-r from-purple-600 to-blue-600 text-white p-4 rounded-full shadow-lg hover:shadow-xl transform hover:scale-110 transition-all duration-300 z-50 hidden"> <i class="fas fa-arrow-up text-xl"></i> </button> <script> // Header scroll effect window.addEventListener('scroll', function() { const header = document.getElementById('header'); if (window.scrollY > 100) { header.classList.add('header-scrolled'); } else { header.classList.remove('header-scrolled'); } }); // Smooth scrolling for anchor links document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function (e) { e.preventDefault(); document.querySelector(this.getAttribute('href')).scrollIntoView({ behavior: 'smooth' }); }); }); // Mobile menu toggle const mobileMenuButton = document.getElementById('mobileMenuButton'); const mobileMenu = document.getElementById('mobileMenu'); const menuIcon = document.getElementById('menuIcon'); if (mobileMenuButton && mobileMenu) { mobileMenuButton.addEventListener('click', function() { mobileMenu.classList.toggle('hidden'); // Toggle icon between bars and times if (mobileMenu.classList.contains('hidden')) { menuIcon.classList.remove('fa-times'); menuIcon.classList.add('fa-bars'); } else { menuIcon.classList.remove('fa-bars'); menuIcon.classList.add('fa-times'); } }); // Close menu when clicking on a link const mobileLinks = mobileMenu.querySelectorAll('a'); mobileLinks.forEach(link => { link.addEventListener('click', function() { mobileMenu.classList.add('hidden'); menuIcon.classList.remove('fa-times'); menuIcon.classList.add('fa-bars'); }); }); } // Brands Carousel const brandsCarousel = document.getElementById('brandsCarousel'); const brandsContainer = document.getElementById('brandsContainer'); const brandsPrevBtn = document.getElementById('brandsPrevBtn'); const brandsNextBtn = document.getElementById('brandsNextBtn'); const brandsPrevBtnMobile = document.getElementById('brandsPrevBtnMobile'); const brandsNextBtnMobile = document.getElementById('brandsNextBtnMobile'); if (brandsContainer && brandsCarousel) { let currentIndex = 0; const cards = brandsContainer.querySelectorAll('.brand-card'); const gap = 24; const cardsPerView = { mobile: 1, tablet: 2, desktop: 3, large: 4 }; function getCardsPerView() { const w = window.innerWidth; if (w >= 1280) return cardsPerView.large; if (w >= 1024) return cardsPerView.desktop; if (w >= 768) return cardsPerView.tablet; return cardsPerView.mobile; } function getCardWidth() { const cpv = getCardsPerView(); const cw = brandsCarousel.offsetWidth; return (cw - gap * (cpv - 1)) / cpv; } function updateCarousel() { const cpv = getCardsPerView(); const cardW = getCardWidth(); const maxIdx = Math.max(0, cards.length - cpv); currentIndex = Math.min(currentIndex, maxIdx); const offset = currentIndex * (cardW + gap); brandsContainer.style.transform = `translateX(-${offset}px)`; const atStart = currentIndex === 0; const atEnd = currentIndex >= maxIdx; [brandsPrevBtn, brandsPrevBtnMobile].forEach(function(btn) { if (btn) { btn.style.opacity = atStart ? '0.4' : '1'; btn.style.pointerEvents = atStart ? 'none' : 'auto'; } }); [brandsNextBtn, brandsNextBtnMobile].forEach(function(btn) { if (btn) { btn.style.opacity = atEnd ? '0.4' : '1'; btn.style.pointerEvents = atEnd ? 'none' : 'auto'; } }); if (document.getElementById('brandsCounter')) { document.getElementById('brandsCounter').textContent = (currentIndex + 1) + ' / ' + (maxIdx + 1); } } function nextSlide() { const cpv = getCardsPerView(); const maxIdx = Math.max(0, cards.length - cpv); if (currentIndex < maxIdx) { currentIndex++; updateCarousel(); } } function prevSlide() { if (currentIndex > 0) { currentIndex--; updateCarousel(); } } if (brandsNextBtn) brandsNextBtn.addEventListener('click', nextSlide); if (brandsPrevBtn) brandsPrevBtn.addEventListener('click', prevSlide); if (brandsNextBtnMobile) brandsNextBtnMobile.addEventListener('click', nextSlide); if (brandsPrevBtnMobile) brandsPrevBtnMobile.addEventListener('click', prevSlide); function setCardWidths() { const cardW = getCardWidth(); cards.forEach(function(card) { card.style.width = cardW + 'px'; card.style.minWidth = cardW + 'px'; card.style.flexShrink = '0'; }); } let touchStartX = 0; let touchEndX = 0; brandsCarousel.addEventListener('touchstart', function(e) { touchStartX = e.changedTouches[0].screenX; }, { passive: true }); brandsCarousel.addEventListener('touchend', function(e) { touchEndX = e.changedTouches[0].screenX; const diff = touchStartX - touchEndX; if (Math.abs(diff) > 50) { diff > 0 ? nextSlide() : prevSlide(); } }, { passive: true }); setCardWidths(); updateCarousel(); let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(function() { setCardWidths(); currentIndex = 0; updateCarousel(); }, 250); }); } </script> <script> // Header scroll effect window.addEventListener('scroll', function() { const header = document.getElementById('header'); if (window.scrollY > 50) { header.classList.add('header-scrolled'); } else { header.classList.remove('header-scrolled'); } }); // Scroll animations const observerOptions = { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }; const observer = new IntersectionObserver(function(entries) { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('visible'); } }); }, observerOptions); // Observe all fade-in-up elements document.querySelectorAll('.fade-in-up').forEach(el => { observer.observe(el); }); // Smooth scroll for anchor links document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function (e) { e.preventDefault(); const target = document.querySelector(this.getAttribute('href')); if (target) { target.scrollIntoView({ behavior: 'smooth', block: 'start' }); } }); }); // Counter animation for stats function animateCounter(element, target, duration = 2000) { let start = 0; const increment = target / (duration / 16); const timer = setInterval(() => { start += increment; if (start >= target) { element.textContent = target + (element.textContent.includes('+') ? '+' : '') + (element.textContent.includes('K') ? 'K€' : ''); clearInterval(timer); } else { element.textContent = Math.floor(start) + (element.textContent.includes('+') ? '+' : '') + (element.textContent.includes('K') ? 'K€' : ''); } }, 16); } // Observe stats section const statsObserver = new IntersectionObserver(function(entries) { entries.forEach(entry => { if (entry.isIntersecting && !entry.target.classList.contains('animated')) { entry.target.classList.add('animated'); const statsCards = entry.target.querySelectorAll('.stats-card'); statsCards.forEach((card, index) => { setTimeout(() => { card.style.opacity = '0'; card.style.transform = 'translateY(20px)'; setTimeout(() => { card.style.transition = 'all 0.6s ease'; card.style.opacity = '1'; card.style.transform = 'translateY(0)'; }, 100); }, index * 100); }); } }); }, { threshold: 0.3 }); const statsSection = document.querySelector('section.bg-gradient-to-r'); if (statsSection) { statsObserver.observe(statsSection); } </script> <script> // Gestion du formulaire de contact const contactForm = document.getElementById('contact-form'); const formMessage = document.getElementById('form-message'); const submitBtn = document.getElementById('submit-btn'); if (contactForm) { contactForm.addEventListener('submit', async function(e) { e.preventDefault(); // Désactiver le bouton pendant l'envoi submitBtn.disabled = true; submitBtn.textContent = 'Envoi en cours...'; // Récupérer les données du formulaire const formData = new FormData(contactForm); try { // Vérifier que les données sont bien dans le FormData const formDataObj = { name: formData.get('name'), email: formData.get('email'), subject: formData.get('subject'), message: formData.get('message') }; console.log('Données du formulaire:', formDataObj); // Vérifier que tous les champs sont remplis if (!formDataObj.name || !formDataObj.email || !formDataObj.subject || !formDataObj.message) { formMessage.classList.remove('hidden'); formMessage.className = 'mb-6 p-4 rounded-lg bg-red-600 text-white'; formMessage.textContent = 'Veuillez remplir tous les champs du formulaire.'; submitBtn.disabled = false; submitBtn.textContent = 'Envoyer le message'; return; } // Essayer d'abord avec JSON (plus fiable) // Siça ne fonctionne pas, on essaiera avec FormData let response; try { // Méthode 1 : Envoyer en JSON (plus fiable selon les forums) response = await fetch('gestion-formulaire-contact/send-email-json.php', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(formDataObj) }); } catch (jsonError) { console.warn('Erreur avec JSON, essai avec FormData:', jsonError); // Méthode 2 : Fallback avec FormData response = await fetch('send-email.php', { method: 'POST', body: formData }); } // Lire le texte de la réponse d'abord pour déboguer const responseText = await response.text(); console.log('Réponse serveur:', responseText.substring(0, 500)); // Vérifier si la réponse est OK if (!response.ok) { // Essayer de parser le JSON d'erreur try { const errorResult = JSON.parse(responseText); // Afficher le message d'erreur du serveur directement à l'utilisateur formMessage.classList.remove('hidden'); formMessage.className = 'mb-6 p-4 rounded-lg bg-red-600 text-white'; formMessage.textContent = errorResult.message || `Erreur ${response.status}: ${response.statusText}`; submitBtn.disabled = false; submitBtn.textContent = 'Envoyer le message'; return; // Sortir de la fonction pour ne pas continuer } catch (e) { throw new Error(`Erreur HTTP ${response.status}: ${response.statusText}. Réponse: ${responseText.substring(0, 200)}`); } } // Vérifier si PHP n'est pas exécuté (le serveur renvoie le code PHP brut) if (responseText.trim().startsWith('<?php') || responseText.includes('<?php')) { console.error('ERREUR: PHP n\'est pas exécuté par le serveur. Le code PHP est renvoyé brut.'); console.error('Le serveur web n\'est pas configuré pour exécuter PHP.'); // Utiliser la solution de secours : sauvegarder dans localStorage const messageData = { name: formData.get('name'), email: formData.get('email'), subject: formData.get('subject'), message: formData.get('message'), timestamp: new Date().toISOString() }; // Sauvegarder dans localStorage comme solution de secours const savedMessages = JSON.parse(localStorage.getItem('vraivex_messages') || '[]'); savedMessages.push(messageData); localStorage.setItem('vraivex_messages', JSON.stringify(savedMessages)); // Afficher un message spécial formMessage.classList.remove('hidden'); formMessage.className = 'mb-6 p-4 rounded-lg bg-yellow-600 text-white'; formMessage.innerHTML = '⚠️ PHP n\'est pas configuré sur le serveur. Votre message aété sauvegardé localement. <br>Veuillez nous contacter directement à <strong>contact@vraivex.fr</strong> ou consulter les messages sauvegardés dans la console du navigateur.'; // Afficher les messages sauvegardés dans la console console.log('Messages sauvegardés localement:', savedMessages); console.log('Pour consulter les messages, tapez dans la console: JSON.parse(localStorage.getItem("vraivex_messages"))'); return; // Sortir de la fonction } // Essayer de parser le JSON let result; try { result = JSON.parse(responseText); } catch (parseError) { console.error('Erreur de parsing JSON:', parseError); console.error('Réponse reçue:', responseText.substring(0, 500)); throw new Error('Le serveur a renvoyé une réponse invalide. Vérifiez la console pour plus de détails.'); } // Afficher le message de résultat formMessage.classList.remove('hidden'); if (result.success) { formMessage.className = 'mb-6 p-4 rounded-lg bg-green-600 text-white'; formMessage.textContent = 'Message envoyé avec succès ! Nous vous répondrons dans les plus brefs délais.'; contactForm.reset(); } else { formMessage.className = 'mb-6 p-4 rounded-lg bg-red-600 text-white'; let errorMsg = result.message || 'Une erreur est survenue. Veuillez réessayer.'; // Afficher le message de debug en développement (à retirer en production) if (result.debug) { console.error('Erreur détaillée:', result.debug); } formMessage.textContent = errorMsg; } } catch (error) { formMessage.classList.remove('hidden'); formMessage.className = 'mb-6 p-4 rounded-lg bg-red-600 text-white'; // Message d'erreur plus détaillé pour le débogage let errorMsg = 'Une erreur est survenue lors de la communication avec le serveur. '; errorMsg += 'Veuillez réessayer plus tard ou nous contacter directement à contact@vraivex.fr'; // En mode développement, afficher plus de détails if (error.message) { console.error('Erreur détaillée:', error); console.error('Message:', error.message); console.error('Stack:', error.stack); } formMessage.textContent = errorMsg; } finally { // Réactiver le bouton submitBtn.disabled = false; submitBtn.textContent = 'Envoyer le message'; // Masquer le message après 5 secondes setTimeout(() => { formMessage.classList.add('hidden'); }, 5000); } }); } // Back to Top Button functionality const backToTopButton = document.getElementById('backToTop'); // Show/hide button based on scroll position window.addEventListener('scroll', () => { if (window.pageYOffset > 300) { backToTopButton.classList.remove('hidden'); } else { backToTopButton.classList.add('hidden'); } }); // Smooth scroll to top when clicked backToTopButton.addEventListener('click', () => { window.scrollTo({ top: 0, behavior: 'smooth' }); }); </script> </body> </html>