Article SEO SEO Technique
```html On-Page SEO : Balises Open Graph et Éléments HTML Essentiels

On-Page SEO : Balises Open Graph et Éléments HTML Essentiels

Introduction

L'optimisation pour les moteurs de recherche (SEO) demeure un enjeu crucial pour tout site web souhaitant se démarquer dans la concurrence féroce d'Internet. Parmi les nombreuses techniques SEO disponibles, les balises Open Graph (OG Tags) jouent un rôle désormais stratégique dans l'amélioration du partage des liens et de la visibilité des pages web sur les réseaux sociaux et dans les résultats de recherche.

Ces éléments HTML spécifiques permettent de contrôler précisément l'apparence des liens partagés en ligne, en influençant directement les images, les titres et les descriptions affichés lorsqu'une page est partagée sur des plateformes comme Facebook, X, LinkedIn ou Bluesky. De plus, les moteurs de recherche comme Google reconnaissent désormais l'importance de ces balises et les utilisent pour afficher les titres dans les résultats de recherche, particulièrement lorsque le contenu de la balise og:title est jugé plus pertinent que la balise title traditionnelle.

Dans ce guide complet et professionnel, nous explorerons en détail le concept des OG Tags SEO, leurs fonctionnalités, les meilleures pratiques pour les implémenter efficacement, et les stratégies nécessaires pour optimiser leur performance et augmenter votre visibilité en ligne.

Concepts Clés des Balises Open Graph

Qu'est-ce que Open Graph ?

Open Graph est une technologie développée initialement par Facebook qui permet aux développeurs et aux éditeurs de contenu de partager des informations structurées sur les pages web avec les plateformes sociales. Ces informations structurées sont utilisées pour afficher les liens partagés de manière plus attractive, informative et engageante. Les OG Tags sont des balises méta spécifiques intégrées dans le code HTML d'une page web, spécifiquement dans la section , pour définir ces informations structurées.

Contrairement aux balises HTML traditionnelles, les OG Tags utilisent le protocole Open Graph qui a été adopté bien au-delà de Facebook, par pratiquement toutes les grandes plateformes de réseaux sociaux. Cette standardisation mondiale en a fait un élément incontournable de toute stratégie de présence en ligne moderne.

Les Types Principaux d'OG Tags

Pour optimiser efficacement votre contenu avec les balises Open Graph, il est essentiel de comprendre les différents types de balises disponibles et leurs fonctions spécifiques :

  • og:title : Définit le titre de la page tel qu'il apparaîtra lorsqu'elle sera partagée sur les réseaux sociaux. Cette balise doit faire entre 55 et 60 caractères pour un affichage optimal sans troncature.
  • og:description : Fournit une brève description du contenu de la page. Cette description complète le titre et doit être concise, informative et inciter au clic.
  • og:image : Spécifie l'image qui sera affichée lors du partage du lien. Cette image doit être de haute qualité, pertinente et avoir un ratio recommandé de 1.91:1.
  • og:url : Indique l'URL canonique de la page, cruciale pour éviter les problèmes de contenu dupliqué.
  • og:type : Définit le type de contenu (article, vidéo, website, product, etc.), permettant aux plateformes d'adapter l'affichage.
  • og:site_name : Affiche le nom officiel du site web, renforçant la reconnaissance de marque.
  • og:locale : Spécifie la langue et la région cible du contenu.

L'Impact des OG Tags sur le SEO Moderne

L'utilisation optimale des OG Tags a un impact significatif direct et indirect sur votre stratégie SEO. Une meilleure présentation des liens partagés augmente considérablement le taux de clic (CTR), ce qui constitue un signal positif pour les moteurs de recherche. Lorsqu'un utilisateur voit un lien avec un titre accrocheur, une description pertinente et une image attrayante, il est naturellement plus enclin à cliquer.

Depuis récemment, Google utilise les balises Open Graph, notamment og:title, pour potentiellement afficher des titres dans les résultats de recherche lorsqu'il juge son contenu plus pertinent que la balise title traditionnelle. Cette évolution renforce l'importance stratégique des OG Tags dans votre approche SEO globale. Une meilleure expérience utilisateur globale, résultant d'une présentation optimisée du contenu, contribue également à réduire le taux de rebond et à améliorer les métriques d'engagement.

Meilleures Pratiques pour Optimiser les OG Tags

Optimiser le Contenu avec des OG Tags Efficaces

Pour maximiser l'efficacité des OG Tags dans votre stratégie SEO et d'engagement social, suivez ces directives éprouvées :

  • Utilisez des titres clairs et attractifs : Le titre og:title doit être entre 55 et 60 caractères, clair et informatif. Contrairement à la balise title, vous pouvez être plus créatif et inclure des éléments comme "Nouveau", "Guide complet" ou "Dernières tendances" pour augmenter l'engagement.
  • Rédigez des descriptions pertinentes : Une description og:description bien rédigée doit résumer précisément le contenu de la page, inclure votre mot-clé cible et inciter l'utilisateur à cliquer. Elle complète le titre en offrant plus de contexte.
  • Sélectionnez des images de haute qualité : L'image og:image doit être pertinente, haute résolution, et avoir des dimensions optimales (1200 x 630 pixels est recommandé). Les images attrayantes et professionnelles augmentent significativement le taux de clic.
  • Assurez-vous que l'URL est correctement configurée : L'URL spécifiée dans og:url doit être l'URL canonique pour éviter les problèmes de contenu dupliqué et de confusion dans les classements.
  • Définissez un type de contenu approprié : Utilisez og:type correctement (article, vidéo, website, product) pour que les plateformes sociales et les moteurs de recherche comprennent la nature de votre contenu.
  • Personnalisez en fonction de l'intention de recherche : Plutôt que de simplement copier la balise title, personnalisez og:title pour cibler des nuances spécifiques d'intentions de recherche. Si votre title cible un mot-clé principal, utilisez og:title pour cibler une variante ou une intention de recherche connexe.

Intégrer des Émotions et des Bénéfices

Les titres qui suscitent une émotion ou soulignent un bénéfice clair sont considérablement plus susceptibles de capter l'attention de l'utilisateur. En intégrant des éléments émotionnels dans votre og:title, comme le fait de répondre à un problème spécifique ou de promettre une solution tangible, vous attirez efficacement les internautes et augmentez le taux de clic dans les résultats de recherche.

Par exemple, au lieu d'un simple titre comme "Guide SEO 2025", optez pour "Comment Maîtriser le SEO en 2025 : Le Guide Complet des Experts". Cette approche renforce l'engagement utilisateur et la probabilité de partage social.

Harmoniser avec le Contenu Réel

Google et les réseaux sociaux valorisent fortement les titres qui reflètent fidèlement le contenu réel de la page. Un titre trompeur ou exagéré peut augmenter le taux de rebond, ce qui aurait un effet inverse sur votre référencement naturel. En effet, le moteur de recherche pourrait dégrader votre position dans les résultats de recherche. La cohérence entre og:title et le contenu réel de votre page est donc cruciale pour maintenir une excellente performance SEO.

Structure HTML et Implémentation Technique

Améliorer la Structure HTML

Inclure les OG Tags dans votre code HTML est essentiel pour garantir une bonne intégration avec les plateformes sociales, les moteurs de recherche et les IA génératives. Voici comment implémenter ces balises correctement dans la section de votre document HTML :



   Votre Titre SEO Optimisé           

    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
 

Votre Titre H1 Principal

Autres Balises HTML Essentielles pour le SEO

Au-delà des OG Tags, d'autres éléments HTML jouent un rôle crucial dans votre stratégie SEO on-page :

  • Balise title : Doit faire entre 30 et 60 caractères, contenir votre mot-clé cible au début et être unique pour chaque page.
  • Meta description : Entre 70 et 160 caractères, elle complète le titre, inclut la requête cible et doit augmenter le taux de clic.
  • Balise H1 : Unique par page, elle doit être claire, accrocheur et contenir des mots-clés pertinents. Elle indique aux moteurs de recherche le sujet principal de la page.
  • Balises H2-H6 : Structurent le contenu de manière logique. Les balises H2 doivent privilégier les formulations sous forme de question et utiliser la requête cible dans 1 à 2 balises sur 3.
  • Balise canonical : Prévient les problèmes de contenu dupliqué en spécifiant l'URL canonique.
  • Balise meta robots : Contrôle l'indexation et le suivi des pages par les moteurs de recherche.

Créer un Contenu de Qualité

L'Importance du Contenu Structuré

Même avec des OG Tags parfaitement optimisés, un contenu de qualité supérieure reste absolument essentiel pour attirer et retenir l'attention des utilisateurs. Les moteurs de recherche, particulièrement avec les avancées récentes en matière d'IA générative, accordent une importance accrue au formatage et à la structure du contenu.

Votre contenu doit impérativement inclure une hiérarchie claire avec des titres bien hiérarchisés, des listes à puces ou numérotées, des tableaux comparatifs, et des paragraphes bien structurés. Le contenu doit également être factuel, clair, objectif et intégrer des sources fiables, des données vérifiées et des citations pertinentes.

Critères de Qualité pour le Contenu

Pour maximiser votre performance SEO, assurez-vous que vos articles respectent ces critères fondamentaux :

  • Longueur et profondeur : Un contenu de plus de 300 mots est recommandé. Les articles longs et détaillés répondent mieux aux requêtes des utilisateurs et reçoivent généralement un meilleur classement.
  • Structure claire : Un plan bien organisé avec une hiérarchie logique des titres facilite la lecture et améliore l'accessibilité.
  • Réponse à l'intention de recherche : Votre contenu doit répondre précisément à la requête de l'internaute et satisfaire complètement son besoin informatif.
  • Optimisation sémantique : Utilisez des synonymes, élargissez votre champ lexical et intégrez des variantes de votre mot-clé principal.
  • Mise à jour régulière : Les pages clés doivent être mises à jour régulièrement pour maintenir leur pertinence et leur autorité.

Outils et Ressources pour Optimiser vos OG Tags

Outils de Vérification et de Monitoring

Pour surveiller efficacement et améliorer l'efficacité de vos OG Tags, plusieurs outils spécialisés sont à votre disposition :

  • Google Search Console : Analyse la présence en ligne complète de votre site et fournit des informations détaillées sur les performances SEO, les impressions, les clics et la position moyenne de votre site dans les résultats de recherche.
  • Google Analytics : Mesure précisément le trafic organique et le comportement des utilisateurs sur votre site, permettant d'identifier les pages les plus performantes et les domaines d'amélioration.
  • Open Graph Debugger : Outil gratuit historiquement développé par Facebook (Meta) pour tester vos OG Tags en temps réel, identifier les problèmes d'implémentation et prévisualiser l'apparence de vos liens lorsqu'ils sont partagés.
  • Silktide Open Graph Checker : Un outil utile et gratuit pour vérifier vos balises Open Graph et obtenir des recommandations d'optimisation spécifiques.
  • Validateurs HTML : Assurent que votre code HTML est valide et que vos balises sont correctement intégrées.

Questions Fréquemment Posées

Pourquoi les OG Tags sont-ils importants pour le SEO ?

L'utilisation correcte des OG Tags permet une meilleure présentation des liens partagés sur les réseaux sociaux et améliore l'expérience utilisateur globale. Cela augmente le taux de clic, réduit le taux de rebond et envoie des signaux positifs aux moteurs de recherche. De plus, Google reconnaît maintenant l'importance de ces balises et peut les utiliser pour afficher les titres dans les résultats de recherche.

Où doivent être placés les OG Tags dans le code HTML ?

Ces balises doivent impérativement être intégrées dans la section du code HTML de chaque page web, jamais dans le corps . Elles doivent être positionnées après les balises meta standards comme charset et viewport.

Les OG Tags influencent-ils directement le classement SEO ?

Bien que ne constituant pas un facteur direct de classement comme les backlinks ou la qualité du contenu, les OG Tags influencent indirectement votre SEO en améliorant le taux de clic et l'engagement utilisateur. Une meilleure présentation du contenu encourage davantage de partages, de clics et d'interactions, ce qui renforce positivement votre performance SEO globale.

Quelle est la différence entre og:title et la balise title ?

La balise </code> est affichée dans l'onglet du navigateur et dans les résultats de recherche Google. L'og:title est affichée lorsqu'un utilisateur partage votre lien sur les réseaux sociaux. Alors que la balise title doit prioritairement optimiser pour les moteurs de recherche, og:title doit optimiser pour l'engagement social.</p> <h3 id="les-og-tags-sont-ils-importants-pour-les-sites-e-commerce">Les OG Tags sont-ils importants pour les sites e-commerce ?</h3> <p>Oui, particulièrement importants ! Pour les e-commerce, les OG Tags permettent de présenter les produits de manière attrayante lors du partage. Une image de produit de haute qualité, une description engageante et un prix compétitif affichés via les OG Tags peuvent augmenter significativement les clics et les conversions.</p> <h2 id="conclusion">Conclusion</h2> <p>Les balises Open Graph constituent un élément stratégique incontournable de toute stratégie SEO moderne efficace. En optimisant vos OG Tags selon les meilleures pratiques présentées dans ce guide, vous améliorez considérablement la visibilité de votre contenu sur les réseaux sociaux et renforcez votre performance dans les résultats de recherche.</p> <p>L'implémentation correcte des OG Tags offre aux entreprises une opportunité unique de tester différentes approches pour leurs titres et descriptions sans affecter directement la balise title traditionnelle. En travaillant ces deux aspects en harmonie, vous augmentez simultanément l'engagement social et le taux de clic dans les résultats de recherche, créant ainsi un effet synergique bénéfique pour votre présence en ligne globale.</p> <p>Assurez-vous de régulièrement auditer vos OG Tags, de tester différentes variations et d'analyser leur impact sur votre trafic et votre engagement. L'optimisation continue de ces éléments, associée à un contenu de qualité et à une stratégie SEO technique solide, positionne votre site pour un succès durable dans l'environnement numérique de 2025 et au-delà.</p> </article> </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="on-page-seo-balises-open-graph-et-elements-html-essentiels"> <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 = 'on-page-seo-balises-open-graph-et-elements-html-essentiels'; // 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="/#brands" class="hover:text-white transition text-sm">Nos marques</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">© 2025 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 cardsPerView = { mobile: 1, tablet: 2, desktop: 3, large: 4 }; function getCardsPerView() { const width = window.innerWidth; if (width >= 1280) return cardsPerView.large; if (width >= 1024) return cardsPerView.desktop; if (width >= 768) return cardsPerView.tablet; return cardsPerView.mobile; } function updateCarousel() { const cardsPerViewCount = getCardsPerView(); const containerWidth = brandsCarousel.offsetWidth; const cardWidth = containerWidth / cardsPerViewCount; const maxIndex = Math.max(0, cards.length - cardsPerViewCount); currentIndex = Math.min(currentIndex, maxIndex); brandsContainer.style.transform = `translateX(-${currentIndex * cardWidth}px)`; // Update button states const isAtStart = currentIndex === 0; const isAtEnd = currentIndex >= maxIndex; if (brandsPrevBtn) { brandsPrevBtn.style.opacity = isAtStart ? '0.5' : '1'; brandsPrevBtn.style.cursor = isAtStart ? 'not-allowed' : 'pointer'; } if (brandsNextBtn) { brandsNextBtn.style.opacity = isAtEnd ? '0.5' : '1'; brandsNextBtn.style.cursor = isAtEnd ? 'not-allowed' : 'pointer'; } if (brandsPrevBtnMobile) { brandsPrevBtnMobile.style.opacity = isAtStart ? '0.5' : '1'; brandsPrevBtnMobile.style.cursor = isAtStart ? 'not-allowed' : 'pointer'; } if (brandsNextBtnMobile) { brandsNextBtnMobile.style.opacity = isAtEnd ? '0.5' : '1'; brandsNextBtnMobile.style.cursor = isAtEnd ? 'not-allowed' : 'pointer'; } } function nextSlide() { const cardsPerViewCount = getCardsPerView(); const maxIndex = Math.max(0, cards.length - cardsPerViewCount); if (currentIndex < maxIndex) { currentIndex++; updateCarousel(); } } function prevSlide() { if (currentIndex > 0) { currentIndex--; updateCarousel(); } } // Event listeners if (brandsNextBtn) brandsNextBtn.addEventListener('click', nextSlide); if (brandsPrevBtn) brandsPrevBtn.addEventListener('click', prevSlide); if (brandsNextBtnMobile) brandsNextBtnMobile.addEventListener('click', nextSlide); if (brandsPrevBtnMobile) brandsPrevBtnMobile.addEventListener('click', prevSlide); // Set responsive width for cards function setCardWidths() { const cardsPerViewCount = getCardsPerView(); const containerWidth = brandsCarousel.offsetWidth; const gap = 24; // 24px gap const cardWidth = (containerWidth - (gap * (cardsPerViewCount - 1))) / cardsPerViewCount; cards.forEach(card => { card.style.width = `${cardWidth}px`; card.style.flexShrink = '0'; }); } // Initialize setCardWidths(); updateCarousel(); // Update on resize 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>