Article SEO SEO Technique

Densité de mots-clés

On-Page SEO : Densité de mots-clés comme métrique d’optimisation

Introduction

La densité de mots-clés est une métrique classique du SEO on-page. Elle mesure la fréquence d’apparition d’un mot-clé dans un contenu par rapport au nombre total de mots de la page. Utilisée comme indicateur et non comme objectif absolu, elle aide à vérifier que le texte met bien en avant son sujet sans tomber dans le bourrage de mots-clés.

Avec l’évolution des algorithmes des moteurs de recherche, la densité n’est plus un facteur de classement direct et central. Elle reste toutefois utile comme garde-fou pour éviter deux extrêmes : un mot-clé quasi absent (manque de clarté thématique) ou un texte artificiellement surchargé (risque de spam et mauvaise expérience utilisateur).

Cet article explique en détail ce qu’est la densité de mots-clés en SEO, comment la calculer, quelles plages de densité viser en 2024‑2025, comment intégrer vos mots-clés dans la page, et comment l’utiliser sans nuire à la qualité éditoriale. Vous verrez également comment la densité s’inscrit dans une approche plus moderne : couverture sémantique, intention de recherche et pertinence globale.

Concepts clés

Définition de la densité de mots-clés

La densité de mots-clés désigne le pourcentage que représente un mot-clé (ou une expression-clé) par rapport au nombre total de mots d’un contenu. Elle se calcule pour un mot-clé principal ou pour un groupe de mots précis (par exemple « densité de mots-clés », « SEO on-page », etc.).

Formellement, on utilise la formule suivante :

  • Densité (%) = (Nombre d’occurrences du mot-clé / Nombre total de mots) × 100

Exemple simple : si un article comporte 2 000 mots et que votre mot-clé principal apparaît 20 fois, la densité est de :

(20 / 2000) × 100 = 1 %

Autre exemple : un contenu de 500 mots dans lequel un mot-clé est répété 10 fois présente également une densité de 2 %.

Clarifier quelques idées reçues

Il est important de corriger plusieurs idées dépassées encore très répandues :

  • Il n’existe pas de densité “idéale universelle”. Aucune valeur (3 %, 5 %, etc.) ne garantit un bon classement à elle seule.
  • Google ne publie aucun seuil officiel. Les moteurs de recherche parlent de bourrage de mots-clés, mais sans seuil chiffré.
  • La densité est aujourd’hui un signal secondaire, parfois pris en compte par des outils, mais largement supplanté par la compréhension sémantique, la pertinence du contenu et l’expérience utilisateur.
  • Le bourrage de mots-clés n’est pas défini par un pourcentage précis, mais par un usage excessif et artificiel qui dégrade la lisibilité.

En pratique, la densité est donc surtout utile pour contrôler votre contenu (éviter les excès) et pour rester conscient de la place réelle de vos mots-clés dans le texte.

Ordres de grandeur réalistes en 2024‑2025

Les recommandations récentes convergent vers des plages de densité largement plus basses que ce que l’on préconisait il y a encore quelques années.

On trouve majoritairement les repères suivants :

  • Environ 0,5 % à 2 % pour la plupart des contenus optimisés de manière naturelle.
  • Très souvent, des contenus qui performent bien restent plutôt dans une fourchette 0,5 % à 1 %.
  • Au‑delà de 2 % à 3 %, le risque d’over‑optimization augmente fortement, surtout si la répétition est visible et peu naturelle.

Ces chiffres sont des lignes directrices issues de la pratique et de l’observation de nombreux sites. Ils ne constituent pas une règle de classement officielle. L’objectif n’est pas de viser un pourcentage précis, mais de rester dans une zone raisonnable et surtout de privilégier un texte fluide, riche et utile.

Localisation et importance des mots-clés

La densité brute ne suffit pas à décrire la façon dont un mot-clé est utilisé. Sa position dans la page joue également un rôle dans la compréhension de votre contenu par les moteurs et les utilisateurs. Idéalement, votre mot-clé principal doit apparaître :

  • dans la balise </strong> (titre SEO de la page),</li> <li>dans le <strong>H1</strong> (titre principal visible),</li> <li>dans la <strong>meta description</strong> (si possible, de manière naturelle),</li> <li>dans au moins un <strong>H2 ou H3</strong> pertinent,</li> <li>dans le <strong>premier paragraphe</strong> ou très tôt dans le contenu,</li> <li>puis ponctuellement dans le reste du texte, sans répétitions forcées.</li> </ul> <p>C’est ce que certains appellent une approche <em>pondérée</em> de la densité : une occurrence dans un titre, un sous-titre ou l’introduction « pèse » généralement plus qu’une occurrence perdue dans un long paragraphe.</p> <h3 id="densite-et-mots-cles-semantiques">Densité et mots-clés sémantiques</h3> <p>Les moteurs modernes comprennent aujourd’hui bien mieux le <strong>contexte</strong> et le <strong>champ sémantique</strong> qu’il y a quelques années. Plutôt que de répéter mécaniquement la même expression, il est préférable d’utiliser :</p> <ul> <li>des <strong>synonymes</strong> (« référencement naturel » / « SEO », « mot-clé » / « expression-clé »),</li> <li>des <strong>variantes</strong> (singulier/pluriel, formes conjuguées, formulations proches),</li> <li>des <strong>mots-clés associés</strong> (densité, optimisation on-page, balises title, méta description, intentions de recherche, etc.),</li> <li>des <strong>entités et concepts liés</strong> au sujet (algorithmes, expérience utilisateur, taux de clic, SERP, etc.).</li> </ul> <p>On parle parfois de « mots-clés LSI » dans le langage marketing, mais ce terme ne correspond pas à un mécanisme officiellement utilisé tel quel par Google. L’important est de <strong>couvrir le sujet</strong> avec un vocabulaire riche, varié et cohérent.</p> <h2 id="bonnes-pratiques-pour-la-densite-de-mots-cles">Bonnes pratiques pour la densité de mots-clés</h2> <h3 id="optimiser-le-contenu-sans-viser-un-pourcentage-fixe">Optimiser le contenu sans viser un pourcentage fixe</h3> <p>L’optimisation de la densité de mots-clés commence par une <strong>recherche de mots-clés pertinente</strong>. Identifiez :</p> <ul> <li>un <strong>mot-clé principal</strong> (par exemple « densité de mots-clés SEO » ou « keyword density on-page »),</li> <li><strong>2 à 4 mots-clés secondaires</strong> ou expressions associées (par exemple « calcul densité mots-clés », « sur-optimisation SEO », « mots-clés sémantiques », « optimisation on-page »).</li> </ul> <p>Vous pouvez utiliser des outils spécialisés pour estimer la demande et la concurrence, mais l’essentiel est de choisir des expressions qui correspondent réellement à <strong>l’intention de recherche</strong> de votre audience.</p> <p>Une fois vos mots-clés définis, rédigez d’abord un <strong>texte complet et naturel</strong> en répondant au mieux à la question de l’internaute (ici : comprendre et utiliser la densité de mots-clés). Ce n’est qu’ensuite que vous ajusterez, si nécessaire, la fréquence d’apparition de vos expressions.</p> <p>Quelques repères pratiques :</p> <ul> <li>Sur un texte de 1 000 mots, une présence du mot-clé principal <strong>entre 5 et 15 fois</strong> correspond généralement à une densité d’environ 0,5 % à 1,5 %, ce qui est souvent suffisant.</li> <li>Sur 2 000 mots, vous pouvez viser un ordre de grandeur similaire (10 à 30 occurrences), en gardant un style rédactionnel fluide.</li> </ul> <p>L’idée n’est jamais de remplir une « case » de densité, mais de <strong>vérifier</strong> que votre mot-clé est effectivement présent et que le contenu reste lisible.</p> <h3 id="ameliorer-la-structure-de-la-page">Améliorer la structure de la page</h3> <p>Une page bien structurée renforce à la fois l’expérience utilisateur et la compréhension par les moteurs. Pour le SEO on-page, soignez particulièrement :</p> <ul> <li>la balise <strong>H1</strong> : un seul H1 par page, contenant votre mot-clé principal ou une variante très proche ;</li> <li>les <strong>H2 et H3</strong> : structurez vos sous-parties de manière logique, avec éventuellement le mot-clé principal et surtout des mots-clés secondaires et sémantiques ;</li> <li>les <strong>paragraphes courts</strong> et aérés, permettant de lire facilement sur mobile ;</li> <li>les <strong>listes à puces</strong> ou numérotées pour mettre en avant les points clés (bonnes pratiques, étapes, erreurs à éviter, etc.).</li> </ul> <p>Une bonne structure permet également d’intégrer vos mots-clés à des endroits stratégiques sans forcer la répétition. Par exemple, un H2 « Comment calculer la densité de mots-clés » ou « Éviter la sur-optimisation de la densité de mots-clés » contribue naturellement à une bonne optimisation on-page.</p> <h3 id="creer-un-contenu-de-qualite-oriente-utilisateur">Créer un contenu de qualité orienté utilisateur</h3> <p>Les moteurs de recherche évaluent de plus en plus la <strong>pertinence globale</strong> et la <strong>satisfaction utilisateur</strong> plutôt qu’un simple pourcentage de mots-clés. Pour maximiser vos chances de bien vous classer :</p> <ul> <li>apportez une <strong>réponse complète</strong> aux questions de vos lecteurs (définition, exemples, méthodes, risques, bonnes pratiques, outils, FAQ) ;</li> <li>évitez les textes purement marketing ou survolés qui n’apportent pas de valeur concrète ;</li> <li>utilisez un <strong>langage clair</strong>, accessible, sans jargon inutile, tout en restant précis ;</li> <li>mettez à jour vos informations pour tenir compte des <strong>évolutions des algorithmes</strong> et des pratiques SEO récentes ;</li> <li>intégrez des <strong>médias pertinents</strong> (schémas, captures d’écran, vidéos explicatives) pour enrichir l’expérience si le contexte le permet.</li> </ul> <p>Un contenu riche, bien structuré et orienté vers la résolution des problèmes de l’utilisateur sera plus performant à long terme qu’un texte rédigé uniquement pour « atteindre 1,5 % de densité ».</p> <h3 id="eviter-la-sur-optimisation">Éviter la sur-optimisation</h3> <p>La sur-optimisation survient lorsque l’on tente d’améliorer son classement en forçant l’intégration de mots-clés au détriment de la qualité du texte. Elle peut prendre plusieurs formes :</p> <ul> <li>répétition <strong>trop fréquente</strong> du même mot-clé dans un paragraphe ;</li> <li>formulations <strong>peu naturelles</strong>, construites uniquement pour contenir le mot-clé ;</li> <li>listes artificielles de variantes de mots-clés sans valeur pour le lecteur ;</li> <li>insertion du mot-clé dans des zones où il n’a pas de sens (ancres, alt d’images, etc.).</li> </ul> <p>Pour éviter la sur-optimisation :</p> <ul> <li>maintenez une densité <strong>raisonnable</strong>, en visant en général une fourchette autour de <strong>0,5 % à 2 %</strong> pour vos principaux mots-clés ;</li> <li>variez les <strong>formes et synonymes</strong> de vos expressions (singulier/pluriel, inversions, expressions proches) ;</li> <li>ajoutez du <strong>contenu complémentaire</strong> qui enrichit le sujet (exemples concrets, cas pratiques, questions fréquentes) sans forcer mécaniquement la présence du mot-clé principal ;</li> <li>relisez toujours votre texte à voix haute : s’il sonne artificiel, c’est souvent le signe d’un mot-clé trop répété.</li> </ul> <p>Retenez que le « keyword stuffing » ne commence pas à un pourcentage précis. C’est le <strong>caractère artificiel</strong> de la rédaction qui est problématique. Un texte peut avoir une densité modérée et être sur-optimisé si le mot-clé est mal intégré.</p> <h3 id="nombre-de-mots-cles-par-page">Nombre de mots-clés par page</h3> <p>Les recommandations modernes ne limitent pas strictement le nombre de mots-clés par page, mais donnent des repères pour garder une stratégie claire. Une approche équilibrée consiste à :</p> <ul> <li>cibler <strong>1 mot-clé principal</strong> très clairement défini ;</li> <li>ajouter <strong>2 à 4 mots-clés secondaires</strong> et expressions proches ;</li> <li>développer un <strong>champ lexical riche</strong> autour de ces termes avec des mots-clés contextuels et sémantiques.</li> </ul> <p>Certaines pages pourront cibler un univers plus large, notamment les contenus longs (« cornerstone content », guides complets, dossiers thématiques). L’important est de ne pas diluer totalement le sujet principal : la page doit garder un <strong>axe central clair</strong>.</p> <h2 id="outils-et-ressources-pour-analyser-la-densite">Outils et ressources pour analyser la densité</h2> <h3 id="google-search-console">Google Search Console</h3> <p>Google Search Console ne fournit pas un calcul direct de la densité de mots-clés, mais c’est un outil incontournable pour analyser la <strong>performance globale</strong> de vos pages. Il permet notamment de :</p> <ul> <li>voir les <strong>requêtes</strong> qui déclenchent l’affichage de vos pages,</li> <li>analyser le <strong>taux de clic (CTR)</strong> pour chaque requête,</li> <li>observer les <strong>positions moyennes</strong> et leur évolution dans le temps,</li> <li>détecter des pages qui se positionnent sur certains mots-clés alors que ces derniers sont peu visibles dans le contenu.</li> </ul> <p>En croisant ces informations avec votre analyse de densité, vous pouvez décider d’<strong>ajuster ou non la fréquence</strong> de vos mots-clés sur certaines pages (par exemple lorsque vous apparaissez déjà pour un mot-clé, mais que le contenu n’en parle pas clairement).</p> <h3 id="outils-d-analyse-seo-specialises">Outils d’analyse SEO spécialisés</h3> <p>Plusieurs outils professionnels proposent des fonctionnalités avancées pour mesurer et analyser la densité de mots-clés, ainsi que la structure sémantique globale de vos contenus :</p> <ul> <li><strong>Semrush</strong> : permet d’analyser le contenu des pages, d’identifier les mots-clés les plus fréquents et de comparer votre texte à celui de vos concurrents. Certains modules utilisent des approches proches du <em>TF‑IDF</em> pour évaluer l’importance des termes par rapport au sujet.</li> <li><strong>Ahrefs</strong> : propose une analyse détaillée du contenu via des rapports de pages, avec des informations sur les mots-clés utilisés, le trafic organique estimé et les backlinks. Utile pour confronter la densité de vos contenus à leurs performances réelles.</li> <li><strong>Moz Keyword Explorer</strong> : conçu pour la recherche de mots-clés, il fournit aussi des données sur la difficulté d’une expression et son potentiel de trafic. Couplé à l’analyse de vos pages, il aide à décider s’il est pertinent de renforcer ou non la présence de certains mots-clés.</li> <li><strong>Plugins SEO pour CMS</strong> (par exemple sur WordPress) : nombre d’extensions de référencement indiquent la fréquence d’apparition d’un mot-clé dans un article et donnent une recommandation de densité indicative. Ces repères (par exemple autour de 1 % à 1,5 %) sont à considérer comme des <strong>guides souples</strong>, non comme des règles absolues.</li> </ul> <h3 id="outils-de-verification-de-densite-dedies">Outils de vérification de densité dédiés</h3> <p>Il existe également des outils en ligne spécifiquement dédiés au <strong>calcul de densité de mots-clés</strong>. Leur fonctionnement est généralement similaire :</p> <ul> <li>vous collez l’URL de votre page ou le texte brut ;</li> <li>l’outil compte le <strong>nombre total de mots</strong> et les occurrences de chaque terme ;</li> <li>il fournit un tableau avec les <strong>mots les plus fréquents</strong> et leur densité en pourcentage ;</li> <li>certains distinguent les <strong>expressions à 1, 2 ou 3 mots</strong> pour mieux analyser vos phrases clés.</li> </ul> <p>Ces outils sont utiles pour repérer :</p> <ul> <li>des mots trop présents qui risquent de donner une impression de répétition,</li> <li>des mots-clés importants sous‑représentés,</li> <li>un <strong>déséquilibre sémantique</strong> (par exemple, un terme secondaire qui apparaît plus que le mot-clé principal).</li> </ul> <h2 id="densite-de-mots-cles-et-evolution-des-algorithmes">Densité de mots-clés et évolution des algorithmes</h2> <p>Les moteurs de recherche ont beaucoup évolué depuis l’époque où un score de densité élevé suffisait parfois à améliorer un classement. Aujourd’hui, ils s’appuient sur :</p> <ul> <li>des modèles de <strong>traitement automatique du langage</strong> (NLP) capables de comprendre le sens global d’un texte ;</li> <li>l’analyse des <strong>co‑occurrences</strong> et des <strong>entités nommées</strong> (personnes, marques, lieux, concepts) ;</li> <li>des signaux d’<strong>expérience utilisateur</strong> (temps passé, taux de rebond, interactions sur la page, etc.) ;</li> <li>la <strong>pertinence par rapport à l’intention de recherche</strong> identifiée (informationnelle, transactionnelle, navigationnelle, etc.).</li> </ul> <p>Dans ce contexte, la densité de mots-clés ne constitue plus un <strong>levier principal</strong> de ranking. Elle reste toutefois un outil pratique pour :</p> <ul> <li>vérifier que votre texte aborde explicitement son sujet principal ;</li> <li>éviter d’omettre complètement un terme clé pourtant central pour vos lecteurs ;</li> <li>détecter des cas extrêmes de répétition involontaire.</li> </ul> <p>La meilleure approche consiste donc à considérer la densité comme un <strong>indicateur secondaire</strong>, complémentaire à une stratégie basée sur la qualité, la pertinence et la satisfaction utilisateur.</p> <h2 id="faq">FAQ</h2> <dl> <dt><strong>Quelle est la bonne densité de mots-clés pour le SEO ?</strong></dt> <dd>Il n’existe pas de pourcentage universel garanti, mais dans la pratique, une densité située globalement entre <strong>0,5 % et 2 %</strong> pour votre mot-clé principal est souvent suffisante. De nombreux contenus performants se situent plutôt autour de <strong>0,5 % à 1 %</strong>. L’essentiel est de rester naturel et de répondre pleinement à l’intention de recherche, sans viser un chiffre précis.</dd> <dt><strong>Puis-je utiliser plusieurs mots-clés par page ?</strong></dt> <dd>Oui. Une approche fréquente consiste à cibler <strong>1 mot-clé principal</strong> et <strong>2 à 4 mots-clés secondaires</strong>, complétés par un champ lexical riche et des mots-clés sémantiquement liés. Ce n’est pas une obligation stricte, mais un repère pour garder une thématique claire tout en couvrant le sujet en profondeur.</dd> <dt><strong>Est‑ce dangereux d’avoir une densité trop élevée ?</strong></dt> <dd>Une densité trop élevée peut rendre le texte difficile à lire et donner l’impression de <strong>keyword stuffing</strong> (bourrage de mots-clés). À partir de quelques pourcents, surtout si la répétition est très visible, le risque d’over‑optimization augmente. Il n’existe cependant pas de seuil officiel à partir duquel une pénalité est garantie : ce qui compte est avant tout le caractère naturel ou artificiel du contenu.</dd> <dt><strong>Une bonne densité de mots-clés suffit‑elle à améliorer mon classement ?</strong></dt> <dd>Non. Une densité raisonnable est un <strong>pré‑requis technique</strong>, pas un levier magique. Pour réellement progresser, vous devez proposer un contenu de qualité, bien structuré, qui couvre le sujet en profondeur, s’aligne sur l’intention de recherche, se charge rapidement et offre une bonne expérience utilisateur. La densité ne doit jamais être l’objectif principal de votre stratégie SEO.</dd> <dt><strong>Google recommande‑t‑il un pourcentage précis de densité ?</strong></dt> <dd>Google ne fournit <strong>aucune recommandation chiffrée</strong> en matière de densité de mots-clés. Le moteur met surtout en garde contre le bourrage de mots-clés et encourage la création de contenus utiles, lisibles et pertinents pour les utilisateurs. Les plages de densité parfois citées par les outils ou les experts ne sont que des <strong>recommandations pratiques</strong>, pas des normes officielles.</dd> <dt><strong>Que penser des “mots-clés LSI” pour améliorer la densité ?</strong></dt> <dd>Le terme « LSI » est souvent utilisé en marketing de contenu, mais il ne reflète pas réellement la façon dont les moteurs modernes fonctionnent. L’important n’est pas d’appliquer une méthode LSI spécifique, mais de travailler un <strong>champ sémantique riche</strong> : synonymes, expressions proches, entités et concepts liés. Cela contribue à améliorer la compréhension globale de votre page, plus que la simple répétition d’un mot-clé.</dd> <dt><strong>Comment savoir si ma densité de mots-clés est trop faible ?</strong></dt> <dd>Si votre page a un bon contenu, mais que votre mot-clé principal apparaît très peu (par exemple une ou deux fois dans un texte long), le sujet peut sembler flou aux utilisateurs comme aux moteurs. Dans ce cas, il est utile de vérifier votre densité avec un outil dédié, puis d’ajouter le mot-clé ou ses variantes à des endroits clés : titre, sous-titres, introduction, conclusion, ancres de liens internes pertinents.</dd> </dl> </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="densite-de-mots-cles"> <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 = 'densite-de-mots-cles'; // 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>