Skip to content

Tout pour WordPress, le développement web — et plus encore

🖱 Défilement fluide vers une ancre : CSS, JavaScript et jQuery, trois approches

🖱 Défilement fluide vers une ancre : CSS, JavaScript et jQuery, trois approches

L'utilisateur clique sur un lien avec une ancre, et la page saute instantanément à l'endroit voulu. Ni animation, ni fluidité, ni compréhension de l'endroit où l'on a atterri. Une transition brusque désoriente, surtout sur les longues landing pages et dans la documentation.

Ajouter un défilement fluide vers une ancre prend 5 minutes. Mais le diable est dans les détails: le lien peut venir d'un site externe, l'URL contient déjà un hash, et un en-tête fixe surplombe le contenu et masque l'ancre. Le simple scroll-behavior: smooth ne règle pas ces nuances.

Ci-dessous, trois approches fonctionnelles: du CSS pur, du JavaScript natif et jQuery. Chacune avec du code que vous pouvez récupérer et coller dans votre projet. En plus, nous traitons séparément les transitions externes, pour que le défilement se déclenche même quand l'utilisateur arrive d'une autre page ou des résultats de recherche.

💡 Aperçu rapide:

  • Activer le défilement fluide en une seule ligne de CSS, sans aucun JavaScript
  • Ajouter un décalage sous un en-tête fixe via scroll-margin-top
  • Écrire du JS natif avec scrollIntoView, pour les transitions externes et le réglage fin
  • Décortiquer la variante jQuery avec animate(), quand il faut une vitesse personnalisée et du contrôle
  • Traiter le hash externe: la page est chargée, l'ancre est dans l'URL, et on glisse en douceur vers la cible

Pourquoi un défilement fluide en général

Un saut brusque vers une ancre désoriente. L'utilisateur perd le contexte: où étais-je, où ai-je atterri, qu'ai-je manqué? Le défilement fluide préserve l'orientation spatiale, l'œil a de quoi s'accrocher pendant que la page bouge.

Du point de vue de la perception, un site avec défilement fluide paraît soigné. C'est particulièrement visible sur les pages longues: landing pages, documentation, sections FAQ, sommaires d'articles. L'utilisateur clique sur «Tarifs» dans le menu, et la page ne sursaute pas, mais glisse doucement vers le bloc des prix.

Techniquement, la tâche se scinde en deux: le défilement au clic sur un lien interne (l'utilisateur est déjà sur la page) et le défilement lors d'une transition externe (l'URL contient d'emblée un #anchor). Dans le second cas, le navigateur saute par défaut à l'ancre instantanément, avant même le chargement complet, et il faut intercepter cela.

CSS scroll-behavior, une ligne au lieu d'un script

La méthode la plus simple et la plus moderne. Les navigateurs ont appris le défilement fluide nativement, une seule règle CSS suffit:

1html {
2 scroll-behavior: smooth;
3}

Voilà. Toute navigation par lien d'ancre devient fluide. Aucun JavaScript, aucune bibliothèque.

Prise en charge, tous les navigateurs modernes depuis 2022: Chrome 61+, Firefox 36+, Safari 15.4+, Edge 79+. La seule exception, IE11, mais sa part de trafic tend vers zéro.

En-tête fixe: scroll-margin-top

Si un en-tête fixe (sticky header) surplombe le contenu, l'ancre se retrouvera dessous après le défilement. Cela se règle en une ligne:

1h2, h3, [id] {
2 scroll-margin-top: 80px; /* hauteur de l'en-tête + décalage */
3}

Le navigateur ajoutera lui-même le décalage lors du défilement vers l'élément cible. Auparavant, on écrivait pour cela des bidouilles avec padding-top et une margin négative, désormais inutiles.

Limites du CSS pur

L'approche CSS n'offre aucun contrôle sur la vitesse d'animation ni sur la fonction de fluidité (easing). Le navigateur utilise une courbe intégrée, généralement ease-in-out. Si vous avez besoin d'une vitesse personnalisée ou d'une animation non linéaire, on passe au JavaScript.

JavaScript natif: scrollIntoView

Quand le CSS seul ne suffit pas, la méthode scrollIntoView() avec l'option behavior: 'smooth' vient à la rescousse:

1document.querySelectorAll('a[href^="#"]').forEach(anchor => {
2 anchor.addEventListener('click', function(e) {
3 e.preventDefault();
4 const target = document.querySelector(this.getAttribute('href'));
5 if (target) {
6 target.scrollIntoView({
7 behavior: 'smooth',
8 block: 'start'
9 });
10 }
11 });
12});

Ce code intercepte le clic sur tous les liens avec #, annule la navigation standard et lance un défilement fluide. Fonctionne sans jQuery et sans bibliothèques supplémentaires.

L'avantage de l'approche, on peut piloter par programme: changer la vitesse via le CSS scroll-behavior sur <html>, ajouter des conditions (par exemple exclure certains liens), combiner avec scroll-margin-top pour l'en-tête.

L'inconvénient, scrollIntoView ne permet pas de définir la vitesse d'animation directement. Pour une durée de défilement personnalisée (disons 800 ms au lieu des ~500 ms du navigateur), il faut soit jQuery, soit un requestAnimationFrame manuel.

jQuery animate, contrôle total sur l'animation

Si le projet utilise déjà jQuery ou qu'il faut un réglage fin (vitesse, décalage sous l'en-tête, filtrage des liens), voici le script à jour. Ci-dessous une version adaptée de l'approche classique, réécrite pour le jQuery moderne:

1jQuery(document).ready(function($) {
2 // Défilement fluide au clic sur un lien d'ancre
3 $('a[href*="#"]:not([href="#"])').on('click', function() {
4 if (
5 location.pathname.replace(/^\//, '') === this.pathname.replace(/^\//, '')
6 && location.hostname === this.hostname
7 ) {
8 var target = $(this.hash);
9 target = target.length ? target : $('[name="' + this.hash.slice(1) + '"]');
10 if (target.length) {
11 $('html, body').animate({
12 scrollTop: target.offset().top - 80
13 }, 800);
14 return false;
15 }
16 }
17 });
18
19 // Défilement lors d'une transition externe avec un hash dans l'URL
20 if (window.location.hash) {
21 var hash = window.location.hash.substring(1);
22 var $target = $('#' + hash);
23 if ($target.length) {
24 setTimeout(function() {
25 $('html, body').animate({
26 scrollTop: $target.offset().top - 80
27 }, 800);
28 }, 100);
29 }
30 }
31});

Ce qui a changé par rapport aux anciennes versions qui circulent sur les blogs:

  • .bind() remplacé par .on(), .bind() est déclaré obsolète depuis jQuery 3.0 et sera supprimé dans jQuery 4.0.
  • Le sélecteur 'a:not(.spu-clickable)[href*="#"]:not([href="#"])' est simplifié, la classe spécifique .spu-clickable se rapportait à une extension précise (Popups by OptinMonster) et n'est pas nécessaire dans le cas général.
  • jQuery(window).bind("load", ...) retiré, la seconde partie du script pour le hash externe vit désormais dans $(document).ready() et ne requiert pas d'événement load séparé.
  • Ajout d'un délai setTimeout(..., 100), pour que le DOM soit garanti rendu avant le calcul de la position de l'ancre (pertinent pour les pages à chargement différé des images et au contenu dynamique).

Où insérer le code

Trois options au choix:

  • Via le fichier du thème. Ajoutez le script dans functions.php via wp_enqueue_script(), la méthode standard pour un thème enfant.
  • Extension Code Snippets. Insérez le code comme un nouveau snippet à exécution automatique, il ne sera pas perdu lors de la mise à jour du thème.
  • Extension JS personnalisée. Si le site utilise déjà une extension pour insérer du JavaScript arbitraire (par exemple WPCode), ajoutez-y le code.

Traitement de la transition externe avec un hash, analyse détaillée

La partie la plus délicate de la tâche. L'utilisateur suit un lien https://site.com/page/#pricing depuis un autre site ou depuis la recherche. Le navigateur saute par défaut instantanément à #pricing, dès que l'élément apparaît dans le DOM, sans animation et souvent avant le chargement complet de la page.

Le script jQuery ci-dessus règle cela en deux étapes:

  • Détecte la présence d'un hash via window.location.hash. S'il y a un hash, il lit sa valeur (sans #).
  • Cherche la cible et défile. $('#' + hash) trouve l'élément, setTimeout laisse la page se rendre, animate() mène à la cible avec un décalage de 80 pixels.

Le délai de 100 millisecondes, empirique. Sur les pages rapides, 50 ms suffisent, sur les lourdes (landing pages avec vidéo de fond, cartes, graphiques) mieux vaut monter à 200-300 ms. Alternative, attendre l'événement window.load au lieu de document.ready, mais alors le défilement ne se produira qu'après le chargement de toutes les images, ce qui peut traîner.

Que faire si l'ancre ne trouve pas la cible

Parfois le hash dans l'URL pointe vers un élément inexistant. Par exemple, l'utilisateur a fait une faute de frappe ou la page a été modifiée. Dans ce cas, ni l'approche CSS ni l'approche JS ne planteront avec une erreur, il ne se passera simplement rien. Le navigateur ignorera l'ancre inexistante. Le défilement fluide s'exécutera aussi silencieusement (la condition if (target.length) ne laisse pas aller plus loin).

Comparaison des trois approches

Critère

CSS scroll-behavior

JS scrollIntoView

jQuery animate

Complexité de mise en œuvre

1 ligne de CSS

10-15 lignes de JS

25-30 lignes de JS + jQuery

Contrôle de la vitesse

Non

Non (navigateur)

Oui (ms)

Hash externe

Oui, nativement

Requiert du code supplémentaire

Requiert du code supplémentaire

Décalage sous l'en-tête

scroll-margin-top

scroll-margin-top

Manuel dans offset().top

Dépendances

Aucune

Aucune

jQuery 3.x

Prise en charge navigateurs

96%+

96%+

99%+ (avec jQuery)

Pour la plupart des projets modernes, l'approche CSS est optimale, une ligne, zéro dépendance, performance native. S'il faut un décalage sous l'en-tête, on l'obtient avec scroll-margin-top. S'il faut une vitesse personnalisée ou la prise en charge de navigateurs anciens, jQuery avec animate().

Ci-dessous une vidéo avec une démonstration en direct des trois approches, du CSS à jQuery:

⁉️🤔 Questions fréquentes

Pourquoi le défilement fluide ne fonctionne-t-il pas sur les appareils mobiles?

Sur iOS Safari, avant la version 15.4, scroll-behavior: smooth n'était pas pris en charge. Désormais la prise en charge existe (Safari 15.4+, mars 2022). Si le défilement ne fonctionne pas sur les anciens iPhone, ajoutez un fallback JavaScript avec scrollIntoView ou requestAnimationFrame. Vérifiez aussi que le défilement n'est pas bloqué par overflow: hidden sur body, certains menus mobiles l'appliquent à l'ouverture. En juin 2026, la prise en charge globale de scroll-behavior, 96,3% des navigateurs (données Can I Use).

Comment faire un défilement fluide non pas vers une ancre, mais vers un élément arbitraire au clic sur un bouton?

Donnez à l'élément un id et utilisez le même JavaScript, mais lié au bouton et non au lien d'ancre. Par exemple, un bouton «Retour en haut»: document.querySelector('#back-to-top').addEventListener('click', () => window.scrollTo({ top: 0, behavior: 'smooth' })). Pour la variante jQuery: $('#back-to-top').on('click', () => $('html, body').animate({ scrollTop: 0 }, 600)). Aucune ancre n'est nécessaire, on défile vers une coordonnée.

Faut-il inclure jQuery uniquement pour un défilement fluide en 2026?

Non. jQuery pèse environ 87 Ko en version min (30 Ko gzip). Si jQuery n'est utilisé nulle part ailleurs sur le site, l'inclure pour un seul défilement est irrationnel. Prenez l'approche CSS avec scroll-behavior: smooth ou le scrollIntoView natif. jQuery ne se justifie que si le site est déjà bâti dessus (thèmes en jQuery, projets legacy) et que vous voulez ajouter le défilement à une base de code existante.

Peut-on animer le défilement à des vitesses différentes, d'abord vite, puis lentement?

Oui, via $.animate() avec jQuery UI ou via requestAnimationFrame avec une fonction de fluidité (easing). Le animate() standard de jQuery utilise une fonction linéaire ou swing. Pour des courbes non standard, incluez jquery.easing ou écrivez votre propre boucle sur requestAnimationFrame avec une fonction de fluidité personnalisée, par exemple easeInOutQuad. Cela donne le contrôle total sur la vitesse, la trajectoire et ne requiert pas de bibliothèques externes.

Comment vérifier que le défilement fonctionne lors d'une transition externe avec un hash?

Ouvrez un nouvel onglet, collez une URL avec une ancre (par exemple https://votre-site.fr/page/#contacts) et appuyez sur Entrée. La page doit se charger et défiler en douceur vers le bloc. Deuxième option, dans la console du navigateur sur une page ouverte, exécuter location.hash = '#contacts'. Si le défilement est fluide, le script fonctionne. Si la page a sursauté instantanément, le gestionnaire de hash externe n'est pas configuré.

Quelle approche choisir pour votre tâche

Si le site est moderne, sans jQuery, le CSS scroll-behavior: smooth couvre l'écrasante majorité des scénarios. Une ligne, résultat instantané. Ajoutez scroll-margin-top sous l'en-tête, et c'est prêt.

Si le site est sous WordPress avec jQuery dans le thème, le script jQuery avec animate(). Il donne le contrôle sur la vitesse et le décalage, traite les transitions externes, n'entre pas en conflit avec d'autres extensions.

Si vous partez de zéro et ne voulez pas embarquer de bibliothèque, le scrollIntoView() natif avec un gestionnaire de hash externe. Le juste milieu: propre, rapide, sans dépendances.

Prenez le code de l'article, collez-le dans votre projet et vérifiez la transition externe, c'est le seul cas facile à oublier lors des tests.