jQuery / Plugin / jQuery Smooth Scroll

jQuery Smooth Scroll anime un déplacement dans la même page, par exemple href="#beta". Sélectionnez les liens puis appelez .smoothScroll(). speed règle la durée et offset corrige la destination. Prévoyez aussi le focus d’arrivée et le mouvement réduit pour poursuivre la navigation au clavier.

Périmètre et dépendances

Il s’agit de jquery-smooth-scroll de kswedberg, pas d’une bibliothèque de molette au nom voisin. Le fragment href="#beta" doit correspondre à un id="beta" unique dans le même document.

L’exemple fixe la distribution CDN du plugin 2.2.0 et jQuery complet 4.0.0. Chargez jQuery, le plugin puis l’initialisation ; slim omet les effets nécessaires. Le même fichier a été exécuté avec 4.0.0 et 3.7.1 sous Windows Edge. Cela ne valide pas toutes les combinaisons de thèmes et plugins.

Seuls deux liens de menu portant .js-scroll sont enrichis. Native jump et le lien du contenu gardent leur fonctionnement normal pour comparer. Limitez la sélection aux liens réellement destinés à cette navigation.

Exemple complet avec focus et marge

Enregistrez et ouvrez smooth-scroll-demo.html en UTF-8. Aucun serveur ou outil de compilation n’est nécessaire ; Internet charge deux scripts. Des sections hautes et un espace final permettent de vérifier l’alignement.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Same-page smooth scrolling</title>
  <script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-smooth-scroll/2.2.0/jquery.smooth-scroll.min.js"></script>
  <style>
    :root { --anchor-gap: 100px; }
    html { scroll-behavior: auto; }
    body { font: 16px/1.6 sans-serif; margin: 20px; padding-top: var(--anchor-gap); }
    #menu { position: fixed; top: 0; left: 0; right: 0; padding: 12px; background: #e6f4f1; z-index: 10; }
    #menu a, #menu button { display: inline-block; margin: 4px 8px; font: inherit; }
    section { min-height: 700px; max-width: 700px; }
    h2 { scroll-margin-top: var(--anchor-gap); }
    h2:focus { outline: 3px solid teal; }
    footer { min-height: 100vh; }
  </style>
</head>
<body>
  <nav id="menu" aria-label="Sections">
    <a class="js-scroll" href="#alpha">Alpha</a>
    <a class="js-scroll" href="#beta">Beta</a>
    <a href="#native">Native jump</a>
    <button type="button" id="reset">Reset</button>
    <span id="status" role="status">Native links available.</span>
  </nav>
  <h1>Explore the sections</h1>
  <section><h2 id="alpha" tabindex="-1">Alpha</h2><p>First destination. Text remains available without JavaScript.</p></section>
  <section><h2 id="beta" tabindex="-1">Beta</h2><p>Second destination. Tab can continue to <a href="#native">the next destination</a>.</p></section>
  <section><h2 id="native" tabindex="-1">Native destination</h2><p>This menu link is not enhanced by the plugin.</p></section>
  <footer>Enough space to align the last heading below the menu.</footer>
  <script>
    jQuery(function ($) {
      if (typeof $.fn.smoothScroll !== "function") return;
      const $menu = $("#menu"), $status = $("#status"), duration = 600;
      function gap() {
        const value = $menu.outerHeight() + 12;
        document.documentElement.style.setProperty("--anchor-gap", value + "px");
        return value;
      }
      gap(); $(window).on("resize.demo", gap);
      $("#menu a.js-scroll").smoothScroll({
        speed: duration, easing: "swing", autoFocus: true,
        beforeScroll: function (options) {
          options.offset = -gap();
          options.speed = window.matchMedia("(prefers-reduced-motion: reduce)").matches ? 0 : duration;
          $status.text("Moving to " + options.scrollTarget);
        },
        afterScroll: function (options) {
          history.replaceState(null, "", options.scrollTarget);
          $status.text("Arrived at " + options.scrollTarget);
        }
      });
      $("#reset").on("click", function () {
        $("html, body").stop(true, false); window.scrollTo(0, 0);
        history.replaceState(null, "", location.pathname + location.search);
        $status.text("Ready; enhanced links: 2.");
      });
      $status.text("Ready; enhanced links: 2.");
    });
  </script>
</body>
</html>

Après préparation, le menu affiche ceci. Sans JavaScript, Native links available. reste visible et les liens à fragment fonctionnent.

Ready; enhanced links: 2.
  1. Choisissez Beta. Après environ 600ms, Arrived at #beta apparaît et le titre s’arrête 12px sous le menu.
  2. Reset revient en haut et retire le fragment. Placez le focus sur Alpha ou Beta au clavier et appuyez sur Entrée.
  3. Le titre d’arrivée reçoit un contour. Après Beta, Tab mène au lien situé dessous.
  4. Native jump ne passe pas par le plugin et ne change pas son message d’arrivée.
  5. Activez reduce pour le mouvement réduit : durée nulle, même destination et même traitement de fin.
  6. Redimensionnez jusqu’au retour à la ligne du menu et vérifiez que le titre reste visible. Réinitialisez avant de comparer.

tabindex="-1" rend le titre focalisable par programme sans ajouter un arrêt Tab ordinaire. autoFocus=true déplace le focus après le défilement au lieu de le laisser dans le menu.

Durée, coordonnées et historique

speed est ici une durée en millisecondes : une valeur plus petite couvre plus vite la même distance. Changez la constante duration de 600 à 1200 sur la même destination puis rétablissez 600. Pour l’accélération, gardez la durée et comparez swing à linear. Les autres noms demandent une extension adaptée.

offset s’ajoute à la coordonnée cible. Pour laisser de l’espace au-dessus, utilisez une valeur négative, ici la hauteur réelle du menu plus 12px. beforeScroll mesure à nouveau pour tenir compte des retours à la ligne. Une valeur positive avance davantage et peut cacher le titre derrière le menu fixe.

scroll-margin-top sert aux liens natifs, tandis que le plugin calcule un offset numérique. Ne supposez pas que le CSS remplace ce calcul ; vérifiez les deux chemins.

Le plugin empêche normalement l’action par défaut. Après arrivée, history.replaceState() modifie le fragment en remplaçant l’entrée actuelle. Chaque cible n’ajoute donc pas d’étape au bouton Retour. Aucune navigation hashchange n’est implémentée. Une application nécessitant Retour/Avancer doit définir sa propre politique d’historique.

Reset arrête effets et file d’attente puis restaure position, fragment et état. Les changements rapides de cible ont aussi été vérifiés. Cela ne constitue pas une annulation pour tout signal de molette ou toute entrée utilisateur.

Alternative CSS et diagnostic

Pour de simples ancres, scroll-behavior peut suffire. Ce CSS remplace le plugin, sans s’y ajouter. Supprimez les deux scripts externes, l’initialisation, le bouton Reset et l’état. Remplacez la règle html auto existante. Gardez scroll-margin-top et réglez sa marge selon la hauteur réelle du menu.

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

Cette solution utilise les liens et l’historique natifs, sans durée en millisecondes ni callbacks du plugin. La requête média réduit le mouvement. Inversement, l’exemple jQuery met le comportement CSS à auto pour éviter deux systèmes concurrents.

Pour smoothScroll is not a function, examinez les réponses Network et l’ordre de chargement. Une deuxième copie de jQuery chargée après le plugin peut perdre son extension. Dans WordPress, vérifiez d’abord les dépendances enregistrées par le thème.

Sans déplacement, vérifiez ID absent ou dupliqué, href réel et sélection. Pour un alignement incorrect, contrôlez le menu, les images ou polices modifiant la mise en page et l’espace en fin de document. Un conteneur défilant interne exige sa configuration scrollElement et ses propres tests de coordonnées.