Skip to content

Todo para WordPress, el desarrollo web — y mucho más

🖱 Desplazamiento suave al ancla: CSS, JavaScript y jQuery - tres enfoques funcionales

🖱 Desplazamiento suave al ancla: CSS, JavaScript y jQuery - tres enfoques funcionales

El usuario hace clic en un enlace con un ancla y la página salta instantáneamente a la ubicación de destino. Sin animación, sin suavidad, sin comprensión de dónde aterrizó. La transición brusca resulta desorientadora, especialmente en páginas de aterrizaje largas y documentación.

Añadir desplazamiento suave a las anclas lleva 5 minutos. Pero el diablo está en los detalles: el enlace puede venir de un sitio externo, la URL ya tiene un hash y hay un encabezado fijo en la parte superior que cubre el ancla. El scroll-behavior: smooth habitual no resuelve estos matices.

A continuación, tres enfoques funcionales: CSS puro, JavaScript nativo y jQuery. Cada uno con código que puede copiar y pegar en su proyecto. Además, cubrimos por separado las transiciones externas para que el desplazamiento funcione incluso cuando el usuario llega desde otra página o desde los resultados de búsqueda.

💡 Resumen rápido:

  • Active el desplazamiento suave con una línea de CSS, sin nada de JavaScript
  • Añada desplazamiento para el encabezado fijo usando scroll-margin-top
  • Escriba JS nativo con scrollIntoView para transiciones externas y ajustes finos
  • Cubra la variante jQuery con animate() cuando necesite velocidad y control personalizados
  • Gestione el hash externo: página cargada, ancla en la URL y desplazamiento suave hasta el destino

Por qué es importante el desplazamiento suave

Saltar abruptamente a un ancla es desorientador. El usuario pierde el contexto: ¿dónde estaba, dónde aterricé, qué me perdí? El desplazamiento suave mantiene la orientación espacial, dando al ojo algo que seguir mientras la página se mueve.

Desde el punto de vista de la percepción, un sitio con desplazamiento suave se ve pulido. Esto se nota especialmente en páginas largas: páginas de aterrizaje, documentación, secciones de preguntas frecuentes, tablas de contenido de artículos. El usuario hace clic en «Precios» en el menú y la página no da un tirón, sino que se desliza suavemente hasta el bloque de precios.

Técnicamente, la tarea se divide en dos: desplazamiento al hacer clic en un enlace interno (el usuario ya está en la página) y desplazamiento en una transición externa (la URL ya contiene #anchor). En el segundo caso, el navegador por defecto salta al ancla instantáneamente incluso antes de la carga completa, y esto necesita ser interceptado.

CSS scroll-behavior, una línea en lugar de un script

El enfoque más simple y moderno. Los navegadores aprendieron el desplazamiento suave de forma nativa, basta con una sola regla CSS:

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

Eso es todo. Cualquier transición mediante un enlace de ancla se vuelve suave. Sin JavaScript, sin bibliotecas.

Soporte: todos los navegadores modernos desde 2022: Chrome 61+, Firefox 36+, Safari 15.4+, Edge 79+. La única excepción es IE11, pero su cuota de tráfico se acerca a cero.

Encabezado fijo: scroll-margin-top

Si un encabezado fijo (sticky header) cuelga sobre el contenido, el ancla después del desplazamiento terminará debajo de él. Se soluciona con una línea:

1h2, h3, [id] {
2 scroll-margin-top: 80px; /* header height + offset */
3}

El navegador añadirá automáticamente el desplazamiento al hacer scroll hacia el elemento de destino. Antes se escribían trucos con padding-top y margin negativo, ahora ya no es necesario.

Limitaciones del CSS puro

El enfoque CSS no tiene control sobre la velocidad de la animación ni la función de aceleración. El navegador usa una curva incorporada, generalmente ease-in-out. Si necesita velocidad personalizada o animación no lineal, pase a JavaScript.

JavaScript nativo: scrollIntoView

Cuando el CSS por sí solo no es suficiente, el método scrollIntoView() con la opción behavior: 'smooth' viene al rescate:

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});

Este código intercepta los clics en todos los enlaces con #, cancela la transición estándar e inicia el desplazamiento suave. Funciona sin jQuery y sin bibliotecas adicionales.

La ventaja: puede controlarlo programáticamente: cambiar la velocidad mediante CSS scroll-behavior en <html>, añadir condiciones (por ejemplo, excluir ciertos enlaces), combinarlo con scroll-margin-top para el encabezado.

La desventaja: scrollIntoView no permite establecer la velocidad de la animación directamente. Para un tiempo de desplazamiento personalizado (digamos, 800 ms en lugar de los ~500 ms del navegador) necesita jQuery o requestAnimationFrame manual.

JQuery animate, control total sobre la animación

Si el proyecto ya usa jQuery o necesita ajustes finos (velocidad, desplazamiento para el encabezado, filtrado de enlaces), aquí tiene el script actual. A continuación, una versión adaptada del enfoque clásico, reescrita para jQuery moderno:

1jQuery(document).ready(function($) {
2 // Smooth scroll on anchor link click
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 // Scroll on external transition with hash in 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});

Lo que cambió aquí en comparación con versiones antiguas que circulan por blogs:

  • .bind() reemplazado por .on(), .bind() fue declarado obsoleto en jQuery 3.0 y se eliminará en jQuery 4.0.
  • Selector 'a:not(.spu-clickable)[href*="#"]:not([href="#"])' simplificado, la clase específica .spu-clickable relacionada con un plugin concreto (Popups de OptinMonster) y generalmente no es necesaria.
  • jQuery(window).bind("load", ...) eliminado, la segunda parte del script para el hash externo ahora reside en $(document).ready() y no requiere un evento load separado.
  • Se añadió un retardo setTimeout(..., 100) para que el DOM esté garantizado de estar renderizado antes de calcular la posición del ancla (relevante para páginas con carga diferida de imágenes y contenido dinámico).

Dónde insertar el código

Tres opciones para elegir:

  • Mediante el archivo del tema. Añada el script a functions.php mediante wp_enqueue_script(), el método estándar para un tema hijo.
  • Plugin Code Snippets. Inserte el código como un nuevo fragmento con ejecución automática, no se perderá al actualizar el tema.
  • Plugin de JS personalizado. Si el sitio ya usa un plugin para insertar JavaScript arbitrario (por ejemplo, WPCode), añada el código allí.

Manejo de la transición externa con hash, desglose detallado

La parte más delicada de la tarea. El usuario sigue un enlace https://site.com/page/#pricing desde otro sitio o desde una búsqueda. El navegador por defecto salta instantáneamente a #pricing tan pronto como el elemento aparece en el DOM, sin animación y a menudo antes de que la página cargue completamente.

El script jQuery anterior resuelve esto en dos pasos:

  • Detecta la presencia del hash mediante window.location.hash. Si el hash existe, lee su valor (sin #).
  • Encuentra el destino y se desplaza. $('#' + hash) encuentra el elemento, setTimeout da tiempo a la página para renderizarse, animate() conduce al destino con un desplazamiento de 80 píxeles.

El retardo de 100 milisegundos es empírico. En páginas rápidas 50 ms son suficientes, en las pesadas (páginas de aterrizaje con video de fondo, mapas, gráficos) es mejor aumentarlo a 200-300 ms. Alternativa: esperar al evento window.load en lugar de document.ready, pero entonces el desplazamiento solo ocurrirá después de que todas las imágenes se carguen, lo que puede tardar un poco.

Qué hacer si el ancla no encuentra el destino

A veces el hash en la URL apunta a un elemento inexistente. Por ejemplo, el usuario cometió un error tipográfico o la página fue modificada. En este caso, ni el enfoque CSS ni el JS fallarán con un error, simplemente no ocurrirá nada. El navegador ignorará el ancla inexistente. El desplazamiento suave también funcionará silenciosamente (la condición if (target.length) no permitirá que continúe).

Comparación de los tres enfoques

Criterio

CSS scroll-behavior

JS scrollIntoView

jQuery animate

Complejidad de implementación

1 línea CSS

10-15 líneas JS

25-30 líneas JS + jQuery

Control de velocidad

No

No (por defecto del navegador)

Sí (ms)

Hash externo

Sí, de forma nativa

Requiere código adicional

Requiere código adicional

Desplazamiento del encabezado

scroll-margin-top

scroll-margin-top

Manual en offset().top

Dependencias

Ninguna

Ninguna

jQuery 3.x

Soporte de navegadores

96%+

96%+

99%+ (con jQuery)

Para la mayoría de los proyectos modernos, el enfoque CSS es óptimo: una línea, cero dependencias, rendimiento nativo. Si necesita desplazamiento para el encabezado, añada scroll-margin-top. Si necesita velocidad personalizada o soporte para navegadores antiguos, jQuery con animate().

A continuación, un video con demostración en vivo de los tres enfoques, desde CSS hasta jQuery:

⁉️🤔 Preguntas frecuentes

¿Por qué no funciona el desplazamiento suave en dispositivos móviles?

En iOS Safari antes de la versión 15.4, scroll-behavior: smooth no era compatible. Ahora existe soporte (Safari 15.4+, marzo de 2022). Si el desplazamiento no funciona en iPhones antiguos, añada un fallback de JavaScript con scrollIntoView o requestAnimationFrame. También verifique que el desplazamiento no esté bloqueado por overflow: hidden en body, algunos menús móviles lo establecen al abrirse. A junio de 2026, el soporte global para scroll-behavior es del 96.3% de los navegadores (datos de Can I Use).

¿Cómo hacer que el desplazamiento suave no sea a un ancla sino a un elemento arbitrario al hacer clic en un botón?

Asigne al elemento un id y use el mismo JavaScript, pero vinculado a un botón en lugar de a un enlace de ancla. Por ejemplo, botón «Volver arriba»: document.querySelector('#back-to-top').addEventListener('click', () => window.scrollTo({ top: 0, behavior: 'smooth' })). Para la variante jQuery: $('#back-to-top').on('click', () => $('html, body').animate({ scrollTop: 0 }, 600)). No se necesitan anclas, desplazamiento a coordenadas.

¿Es necesario incluir jQuery solo para el desplazamiento suave en 2026?

No. jQuery pesa unos 87 KB en versión minificada (30 KB gzip). Si jQuery no se usa en ningún otro lugar del sitio, incluirlo para un solo desplazamiento es irracional. Tome el enfoque CSS con scroll-behavior: smooth o scrollIntoView nativo. jQuery se justifica solo si el sitio ya está construido sobre él (temas jQuery, proyectos heredados) y desea añadir el desplazamiento a la base de código existente.

¿Se puede animar el desplazamiento con diferente velocidad, rápido al principio y luego lento?

Sí, mediante $.animate() con jQuery UI o mediante requestAnimationFrame con una función de aceleración. El animate() estándar de jQuery usa función lineal o swing. Para curvas no estándar, incluya jquery.easing o escriba su propio bucle en requestAnimationFrame con una función de aceleración personalizada, por ejemplo, easeInOutQuad. Esto da control total sobre la velocidad, la trayectoria y no requiere bibliotecas externas.

¿Cómo verificar que el desplazamiento funciona en la transición externa con hash?

Abra una nueva pestaña, pegue la URL con el ancla (por ejemplo, https://yoursite.com/page/#contacts) y presione Enter. La página debería cargarse y desplazarse suavemente hasta el bloque. Segunda opción: en la consola del navegador en la página abierta, ejecute location.hash = '#contacts'. Si el desplazamiento es suave, el script funciona. Si la página saltó instantáneamente, el manejador de hash externo no está configurado.

Qué enfoque elegir para su tarea

Si el sitio es moderno, sin jQuery, CSS scroll-behavior: smooth cubre la gran mayoría de los escenarios. Una línea, resultado instantáneo. Añada scroll-margin-top para el encabezado y listo.

Si el sitio está en WordPress con jQuery en el tema, script jQuery con animate(). Da control sobre la velocidad y el desplazamiento, maneja transiciones externas, no entra en conflicto con otros plugins.

Si está escribiendo desde cero y no quiere incluir una biblioteca, nativo scrollIntoView() con manejador de hash externo. El punto medio dorado: limpio, rápido, sin dependencias.

Tome el código del artículo, péguelo en su proyecto y verifique la transición externa, este es el único caso que es fácil pasar por alto durante las pruebas.