
🖱 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
scrollIntoViewpara 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:
1 html { 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:
1 h2, 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:
1 document.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:
1 jQuery(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-clickablerelacionada 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 eventoloadseparado.- 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.phpmediantewp_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,setTimeoutda 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 |
|
| Manual en |
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: smoothno 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 conscrollIntoVieworequestAnimationFrame. También verifique que el desplazamiento no esté bloqueado poroverflow: hiddenenbody, algunos menús móviles lo establecen al abrirse. A junio de 2026, el soporte global parascroll-behaviores 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
idy 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: smoothoscrollIntoViewnativo. 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 medianterequestAnimationFramecon una función de aceleración. Elanimate()estándar de jQuery usa función lineal o swing. Para curvas no estándar, incluyajquery.easingo escriba su propio bucle enrequestAnimationFramecon 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, ejecutelocation.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.



