jQuery / Plugin / jQuery Smooth Scroll

jQuery Smooth Scroll anima la navegación dentro de una página, como href="#beta". Seleccione enlaces y llame a .smoothScroll(); speed controla la duración y offset ajusta el destino. Incluya foco en el destino y movimiento reducido para continuar la navegación después del efecto visual.

Alcance y archivos

Se utiliza jquery-smooth-scroll de kswedberg, distinto de bibliotecas de rueda con nombres similares. href="#beta" debe corresponder a un id="beta" único en el mismo documento.

El ejemplo fija el plugin CDN 2.2.0 y jQuery completo 4.0.0. Cargue jQuery, plugin e inicialización en ese orden; slim no incluye los efectos necesarios. Se ejecutó el mismo plugin con 4.0.0 y 3.7.1 en Windows Edge. Esto no valida todas las combinaciones de temas y plugins.

Solo se mejoran los dos enlaces .js-scroll del menú. Native jump y el enlace del contenido mantienen su navegación normal para comparar. Limite el selector a enlaces con esa intención, sin modificar todos los a del sitio.

Ejemplo completo con foco y margen

Guarde smooth-scroll-demo.html en UTF-8 y ábralo. No requiere servidor ni compilación; Internet carga dos scripts. Las secciones altas y el espacio final permiten observar la alineación.

<!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>

Tras inicializar aparece este estado. Sin JavaScript permanece Native links available. y siguen funcionando los enlaces con fragmento.

Ready; enhanced links: 2.
  1. Seleccione Beta. En unos 600ms aparece Arrived at #beta; el título queda 12px debajo del menú.
  2. Reset vuelve arriba y quita el fragmento. Dé foco a Alpha o Beta con el teclado y pulse Enter.
  3. El destino tiene un contorno de foco. Tras Beta, Tab continúa al enlace que está debajo.
  4. Native jump evita el plugin y no modifica su mensaje de llegada.
  5. Active movimiento reducido reduce: la duración es cero, con el mismo destino y finalización.
  6. Cambie el ancho hasta que el menú se divida en líneas y compruebe que el título sigue visible. Reinicie para comparar.

tabindex="-1" permite enfocar el título por programa sin añadir una parada Tab normal. autoFocus=true lleva allí el foco al terminar, en lugar de dejar el teclado en el menú.

Duración, coordenadas e historial

speed es aquí una duración en milisegundos; un valor menor recorre antes la misma distancia. Cambie la constante duration de 600 a 1200 en el mismo destino y vuelva a 600. Para comparar easing mantenga el tiempo y cambie swing por linear. Otros nombres necesitan una extensión adecuada.

offset se suma a la coordenada objetivo. Para dejar espacio superior use un valor negativo: altura real del menú más 12px con signo menos. beforeScroll vuelve a medir y considera los saltos de línea. Un valor positivo avanza más y puede ocultar el título bajo el menú fijo.

scroll-margin-top sirve para los enlaces nativos; el plugin calcula su offset numérico. No suponga que CSS sustituye ese cálculo. Compruebe ambas rutas.

El plugin impide normalmente la acción predeterminada. Al terminar, history.replaceState() cambia el fragmento sustituyendo la entrada actual. Cada destino no añade un paso a Atrás. No se implementó navegación hashchange. Una aplicación que necesite Atrás/Adelante requiere su propia política de historial.

Reset detiene efectos y cola, restaura posición, fragmento y estado. También se probaron cambios rápidos de destino. Esto no implementa cancelación para cualquier rueda u otra entrada del usuario.

Alternativa CSS y diagnóstico

Para anclas sencillas puede bastar scroll-behavior. Este CSS reemplaza el plugin; no se añade junto a él. Quite ambos scripts externos, la inicialización, Reset y el estado. Sustituya la regla html auto. Mantenga scroll-margin-top y ajuste su margen a la altura real del menú.

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

Se usan fragmentos e historial nativos, sin duración en milisegundos ni callbacks del plugin. La consulta multimedia reduce el movimiento. El ejemplo jQuery, en cambio, fija auto en CSS para que no compitan dos sistemas.

Ante smoothScroll is not a function, revise respuestas Network y orden. Cargar otra copia de jQuery después del plugin puede eliminar su extensión. En WordPress revise primero las dependencias registradas por el tema.

Sin movimiento, compruebe ID ausentes o duplicados, href real y selección. Si la posición es incorrecta, revise menú, imágenes o fuentes que cambien el diseño y espacio al final. Un contenedor interno exige configurar scrollElement y probar sus propias coordenadas.