Article SEO SEO Technique

SEO web

SEO web

Introduction au SEO web

Le SEO web représente une discipline incontournable pour toute entreprise ou individu désirant améliorer le classement de son site sur les moteurs de recherche. Dans un univers numérique où Google enregistre plus de 99 000 recherches par seconde, soit environ 8,5 milliards par jour, seule une optimisation rigoureuse permet de capter l'attention des internautes et de générer un trafic qualifié. L'objectif principal du SEO web consiste à améliorer le classement des pages dans les résultats naturels, comme ceux de Google qui détient plus de 92 % des parts de marché en France.

Cette stratégie permet non seulement d'améliorer la visibilité mais aussi d'augmenter le trafic organique, qui représente près de 94 % de tous les clics sur les résultats de recherche. Un trafic organique de qualité se traduit par une hausse des conversions : ventes de produits, génération de leads ou fidélisation d'une audience. Par exemple, les trois premiers résultats captent plus de 68 % des clics, avec la première position obtenant jusqu'à 39,8 % du trafic. Cet article vous guide en profondeur à travers les concepts clés, les bonnes pratiques, les outils essentiels et les tendances 2025 pour réussir votre stratégie de référencement naturel.

En 2025, avec l'essor de l'intelligence artificielle et des recherches locales, le SEO web évolue rapidement. Plus de 30 % des recherches incluent une localisation, et 46 % ont une intention locale. Ignorer ces aspects signifie perdre une part significative de visibilité. Que vous soyez débutant ou expert, ce guide complet vous équipe pour dominer les SERP et maximiser votre présence en ligne.

Concepts clés du SEO web

Pour maîtriser le SEO web, il est fondamental de comprendre ses piliers. Ces concepts forment la base de toute stratégie efficace visant à améliorer le classement et la performance globale d'un site.

  • Référencement naturel (SEO) : Ensemble de techniques gratuites pour optimiser le positionnement sans publicité payante. Il repose sur la pertinence, la qualité et l'autorité du contenu.
  • Algorithmes des moteurs de recherche : Google utilise des algorithmes complexes évaluant plus de 200 facteurs, comme E-E-A-T (Experience, Expertise, Authoritativeness, Trustworthiness), pour classer les pages.
  • Mots-clés : Termes recherchés par les utilisateurs. En 2025, 34,71 % des requêtes comportent quatre mots ou plus, privilégiant les requêtes longues et spécifiques.
  • Expérience utilisateur (UX) : Sites rapides, mobiles et intuitifs. 75 % des utilisateurs ne dépassent jamais la première page des résultats.
  • Authority du site : Mesurée par les backlinks et la crédibilité. Les sites autoritaires bénéficient d'un meilleur positionnement durable.

On-Page SEO : Optimisations internes

L'on-page SEO regroupe toutes les actions réalisées directement sur les pages du site pour améliorer le classement. Ces optimisations impactent directement la pertinence perçue par les moteurs.

  • Optimisation des titres (</code>) : Intégrez le mot-clé principal en début, limitez à 60 caractères pour un CTR optimal.</li> <li>Balises sémantiques (<code><h1></code>, <code><h2 id="structurez-le-contenu-pour-une-hierarchie-claire-favorisant-la-lecture-et-l-indexation-descriptions-"></code>) : Structurez le contenu pour une hiérarchie claire, favorisant la lecture et l'indexation.</li> <li>Descriptions meta : Récapitulatives attractives de 150-160 caractères, incluant mots-clés pour booster le CTR de 15-20 %.</li> <li>Images optimisées : Noms de fichiers descriptifs et attributs alt riches en mots-clés, réduisant le poids pour des Core Web Vitals parfaits.</li> <li>Liens internes : Améliore la navigation et distribue l'autorité des pages, augmentant le temps passé sur site.</li> <li>Contenu de qualité : Articles de plus de 3 000 mots génèrent trois fois plus de trafic que la moyenne, avec 4 fois plus de partages.</li> </ul> <p>En pratique, une fiche produit e-commerce optimisée peut ranker pour des centaines de mots-clés. Une marque moyenne se classe pour 1 783 termes en 2025 grâce à un contenu étoffé : catégories, blogs, FAQ.</p> <h3>Off-Page SEO : Stratégies externes</h2> <p>L'<em>off-page SEO</em> vise à renforcer l'autorité via des signaux externes. Les backlinks restent un facteur clé, représentant l'un des trois piliers de classement avec le contenu et l'intention utilisateur.</p> <ul> <li><strong>Netlinking</strong> : Acquisition de liens de qualité depuis des sites thématiques. Un bon profil de liens booste l'autorité de 30-50 %.</li> <li><strong>Réseaux sociaux</strong> : Partages augmentent la visibilité et génèrent des signaux indirects pour Google.</li> <li><strong>Mentions et citations</strong> : NAP (Name, Address, Phone) cohérent pour le SEO local, crucial car 76 % des recherches locales sur mobile mènent à une visite en 24 heures.</li> <li><strong>Partenariats</strong> : Guest posting et collaborations pour des liens dofollow naturels.</li> <li><strong>Signaux sociaux</strong> : Likes, partages influencent indirectement le trafic et la fraîcheur du contenu.</li> </ul> <p>Évitez les liens toxiques : Google pénalise les pratiques black-hat. Privilégiez la qualité sur la quantité pour un profil durable.</p> <h3 id="technical-seo-fondations-invisibles">Technical SEO : Fondations invisibles</h3> <p>Le <strong>technical SEO</strong> assure l'accessibilité et la performance du site. Sans ces bases, même le meilleur contenu échoue.</p> <ul> <li>Indexation : Sitemap.xml et robots.txt pour guider les crawlers.</li> <li>Vitesse de chargement : Core Web Vitals optimaux, avec Largest Contentful Paint sous 2,5 secondes.</li> <li>Mobile-first : 92,3 % des accès web via mobile en 2025.</li> <li>HTTPS : Sécurité obligatoire pour la confiance et le ranking.</li> <li>Structure des données : Schema.org pour rich snippets, augmentant le CTR jusqu'à 42,9 %.</li> </ul> <h2 id="bonnes-pratiques-pour-ameliorer-la-visibilite-en-seo-web">Bonnes pratiques pour améliorer la visibilité en SEO web</h2> <p>Appliquer ces pratiques concrètes permet d'<strong>améliorer la visibilité</strong> et le trafic organique durablement. Focus sur l'intention utilisateur et les tendances 2025.</p> <h3 id="recherche-et-optimisation-des-mots-cles">Recherche et optimisation des mots-clés</h3> <p>Identifiez des mots-clés à fort volume et faible concurrence. Outils comme Google Keyword Planner révèlent des opportunités : "SEO web" génère des milliers de recherches mensuelles. Intégrez-les naturellement : densité 1-2 %, en titles, headings et contenu.</p> <h3 id="creation-de-contenu-premium">Création de contenu premium</h3> <p>Produisez du contenu utile, original et approfondi. Les articles longs performent mieux : +3x trafic. Intégrez l'IA pour 58 % des marketeurs, mais humanisez pour éviter les pénalités. Mettez à jour régulièrement : fraîcheur booste le ranking.</p> <h3 id="optimisation-mobile-et-vitesse">Optimisation mobile et vitesse</h3> <p>Avec 72,6 % des accès exclusifs mobile, priorisez responsive design. Compressez images, minifiez CSS/JS. Résultat : +15 % de conversions avec bons Core Web Vitals.</p> <h3 id="seo-local-ciblez-pres-de-moi">SEO local : Ciblez "près de moi"</h3> <p>Les recherches "près de moi" ont explosé de 500-900 %. Optimisez Google My Business, NAP cohérent. 28 % aboutissent à un achat.</p> <h3 id="suivi-et-analyse-des-performances">Suivi et analyse des performances</h3> <p>Mesurez CTR (première position : 31-39 %), positions et trafic. Ajustez en continu : passer de 2e à 1re place = +74,5 % clics.</p> <h2 id="outils-et-ressources-indispensables-pour-le-seo-web">Outils et ressources indispensables pour le SEO web</h2> <p>Ces outils gratuits ou freemium simplifient l'optimisation.</p> <ul> <li><strong>Google Search Console</strong> : Indexation, erreurs, performances. Détecte 100 % des problèmes techniques.</li> <li><strong>Google Analytics 4</strong> : Trafic organique, conversions, comportement utilisateur.</li> <li><strong>Ahrefs/SEMrush</strong> : Backlinks, mots-clés, audits (freemium limité).</li> <li><strong>Screaming Frog</strong> : Crawl technique pour sites <500 pages.</li> <li><strong>PageSpeed Insights</strong> : Core Web Vitals et conseils vitesse.</li> </ul> <p>Combine-les pour un dashboard complet. Budget moyen consultant SEO : 37 500 €/an en France.</p> <h2 id="tendances-seo-web-2025">Tendances SEO web 2025</h2> <p>L'IA générative, GEO (Generative Engine Optimization) et recherches sans clic dominent. 63 % voient un impact positif des AI Overviews. Priorisez E-E-A-T, contenu vocal (recherche vocale en hausse) et zero-click. Marché SEO mondial : 147 milliards $.</p> <h2 id="faq-seo-web">FAQ SEO web</h2> <p><strong>Comment améliorer le classement rapidement ?</strong> Optimisez on-page, acquérez backlinks qualité, ciblez longue traîne.</p> <p><strong>Le SEO web est-il gratuit ?</strong> Oui, mais temps/intexpertise requis. ROI élevé : 91 % des pros confirment impact positif.</p> <p><strong>Impact mobile sur SEO ?</strong> Mobile-first indexing : 92 % trafic mobile.</p> <p><strong>Fréquence mises à jour Google ?</strong> Plusieurs milliers/an, focus Helpful Content.</p> <p><strong>SEO local efficace ?</strong> Oui, 67 % préfèrent entreprises locales visibles.</p> <p>Appliquez ces stratégies dès aujourd'hui pour <strong>améliorer la visibilité</strong> et dominer Google en 2025. Contactez un expert pour un audit personnalisé.</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="seo-web"> <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 = 'seo-web'; // 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>