09/09/2023
El desplazamiento o scroll es una de las interacciones más fundamentales y frecuentes en la web. Desde leer un artículo hasta navegar por una galería de imágenes, los usuarios hacen scroll constantemente. Como desarrolladores, entender y manipular los eventos asociados a esta acción nos abre un mundo de posibilidades para crear experiencias de usuario más ricas, interactivas y fluidas. En esta guía completa, exploraremos a fondo los eventos de scroll en JavaScript, desde el clásico evento scroll hasta el moderno y potente evento onwheel, cubriendo sus diferencias, casos de uso prácticos y, lo más importante, cómo optimizar su rendimiento para evitar problemas de fluidez.

El Evento Clásico: `scroll`
El evento scroll es el más antiguo y conocido. Se dispara en un elemento cada vez que su vista de desplazamiento ha sido movida. Aplica principalmente al objeto window, pero también a cualquier elemento que tenga una barra de desplazamiento, como un <div> con la propiedad CSS overflow: scroll.
Su funcionamiento es simple: te notifica que el scroll ha ocurrido. Sin embargo, su simplicidad es también su mayor limitación. No proporciona información detallada sobre la interacción del usuario, como la dirección o la velocidad del desplazamiento con la rueda del ratón; simplemente informa de la nueva posición del scroll.
Ejemplo Básico de `scroll`
Imaginemos un cuadro de texto con contenido desbordado. Podemos detectar cuándo el usuario hace scroll dentro de él.
<!-- HTML --> <div id="miCaja" style="border: 1px solid #ccc; width: 300px; height: 150px; overflow: scroll;"> <p>Este es un contenido muy largo que obliga al usuario a hacer scroll para poder leerlo en su totalidad. A medida que te desplazas, se registrará un evento en la consola. Sigue bajando para ver más texto de ejemplo que llene este contenedor y active la barra de desplazamiento vertical.</p> <p>Párrafo adicional para generar más scroll.</p> </div> <p>Eventos de scroll registrados: <span id="contador">0</span></p> <!-- JavaScript --> <script> const miCaja = document.getElementById('miCaja'); const contadorSpan = document.getElementById('contador'); let contadorEventos = 0; miCaja.addEventListener('scroll', function(evento) { contadorEventos++; contadorSpan.textContent = contadorEventos; console.log('¡Se ha hecho scroll!'); console.log('Posición de scroll vertical:', miCaja.scrollTop); }); </script>En este ejemplo, cada vez que el usuario mueve la barra de desplazamiento del div, el evento se dispara y actualizamos un contador. Es útil para acciones como cargar más contenido de forma perezosa (infinite scroll) o para activar animaciones cuando un elemento entra en la vista (viewport).
La Evolución: El Evento `onwheel`
Para interacciones más finas y detalladas, especialmente las que involucran la rueda del ratón o el trackpad, el evento `onwheel` es la herramienta moderna y preferida. A diferencia de scroll, que se activa por el cambio de posición de la barra de desplazamiento, onwheel se dispara directamente por la acción de girar la rueda del ratón.
Esta distinción es crucial. El evento onwheel nos proporciona datos precisos sobre la interacción del usuario antes de que el scroll ocurra, lo que nos permite interceptarlo, modificarlo o usarlo para fines completamente diferentes, como hacer zoom, rotar un objeto 3D o navegar por un carrusel horizontal.
Propiedades Clave del `WheelEvent`
Cuando se dispara un evento onwheel, el objeto WheelEvent que se pasa a la función manejadora contiene información valiosísima. Estas son sus propiedades más importantes:
| Propiedad | Tipo | Descripción |
|---|---|---|
deltaX | Number | La cantidad de desplazamiento horizontal. Un valor positivo indica scroll hacia la derecha; negativo, hacia la izquierda. |
deltaY | Number | La cantidad de desplazamiento vertical. Un valor positivo indica scroll hacia abajo; negativo, hacia arriba. Es la propiedad más comúnmente utilizada. |
deltaZ | Number | La cantidad de desplazamiento en el eje Z. Generalmente es cero para ratones convencionales. |
deltaMode | Number | Indica la unidad de los valores delta. 0 para píxeles, 1 para líneas, 2 para páginas. |
Casos de Uso Prácticos con `onwheel`
1. Zoom de una Imagen
Podemos usar el valor de `deltaY` para controlar el nivel de zoom sobre un elemento, como una imagen en un canvas. Para evitar que la página entera se desplace al mismo tiempo, usamos event.preventDefault().
<!-- HTML --> <canvas id="lienzoZoom" width="400" height="300" style="border: 1px solid black;"></canvas> <!-- JavaScript --> <script> const lienzo = document.getElementById('lienzoZoom'); const ctx = lienzo.getContext('2d'); const imagen = new Image(); imagen.src = 'https://dummyimage.com/150x150/007bff/fff'; let escala = 1.0; imagen.onload = () => { ctx.drawImage(imagen, 125, 75); lienzo.addEventListener('wheel', function(evento) { // Prevenimos el comportamiento por defecto (scroll de la página) evento.preventDefault(); // Aumentamos o disminuimos la escala según la dirección del scroll escala += evento.deltaY * -0.005; // Limitamos la escala a un mínimo y un máximo escala = Math.min(Math.max(0.5, escala), 4); // Limpiamos y redibujamos la imagen con la nueva escala ctx.clearRect(0, 0, lienzo.width, lienzo.height); ctx.save(); ctx.translate(lienzo.width / 2, lienzo.height / 2); ctx.scale(escala, escala); ctx.translate(-lienzo.width / 2, -lienzo.height / 2); ctx.drawImage(imagen, 125, 75); ctx.restore(); }); }; </script>2. Scroll Horizontal Personalizado
Una de las aplicaciones más populares es convertir el scroll vertical del ratón en un desplazamiento horizontal para un carrusel o una galería. Esto se puede lograr fácilmente sumando el valor de deltaY a la propiedad scrollLeft del contenedor.

<!-- HTML --> <div id="galeriaHorizontal" style="border:1px solid #ccc; width:100%; overflow-x: scroll; white-space: nowrap;"> <div style="display: inline-block; width: 300px; height: 200px; background: lightblue; margin: 10px;"></div> <div style="display: inline-block; width: 300px; height: 200px; background: lightcoral; margin: 10px;"></div> <div style="display: inline-block; width: 300px; height: 200px; background: lightgreen; margin: 10px;"></div> <div style="display: inline-block; width: 300px; height: 200px; background: lightgoldenrodyellow; margin: 10px;"></div> </div> <!-- JavaScript --> <script> const galeria = document.getElementById('galeriaHorizontal'); galeria.addEventListener('wheel', function(evento) { // Si hay scroll vertical, prevenimos el comportamiento por defecto if (evento.deltaY !== 0) { evento.preventDefault(); // Sumamos el valor de deltaY a la posición de scroll horizontal galeria.scrollLeft += evento.deltaY; } }); </script>Optimización y Rendimiento: El Desafío del Scroll
Los eventos de scroll, tanto scroll como onwheel, pueden dispararse a una frecuencia muy alta, a menudo decenas de veces por segundo. Si el código dentro de nuestro manejador de eventos es computacionalmente costoso (por ejemplo, realiza manipulaciones complejas del DOM, cálculos pesados o peticiones de red), puede causar problemas de rendimiento, conocidos como jank o tartamudeo, arruinando la experiencia del usuario.
La solución a este problema es una técnica llamada throttling. Consiste en limitar la frecuencia con la que se ejecuta nuestra función, sin importar cuántas veces se dispare el evento. Una forma sencilla de implementarlo es con setTimeout.
Implementando Throttling en un Evento de Scroll
El siguiente código asegura que nuestra función hacerAlgoCostoso no se ejecute más de una vez cada 100 milisegundos, incluso si el usuario está haciendo scroll frenéticamente.
<script> let ultimaPosicionScroll = 0; let enProceso = false; // Una bandera para controlar la ejecución function hacerAlgoCostoso(posicionScroll) { // Aquí iría tu lógica compleja: animaciones, cambios en el DOM, etc. console.log('Ejecutando lógica con scroll en:', posicionScroll); } document.addEventListener('scroll', function() { ultimaPosicionScroll = window.scrollY; if (!enProceso) { enProceso = true; setTimeout(() => { hacerAlgoCostoso(ultimaPosicionScroll); enProceso = false; // Liberamos la bandera para la próxima ejecución }, 100); // Esperamos 100ms antes de la próxima ejecución } }); </script>Esta técnica es fundamental para mantener nuestras aplicaciones web fluidas y responsivas cuando trabajamos con eventos de alta frecuencia.
Preguntas Frecuentes (FAQ)
¿Cuál es la diferencia principal entre el evento `scroll` y `onwheel`?
La diferencia clave radica en el origen. El evento scroll se dispara cuando la posición de la barra de desplazamiento de un elemento cambia. El evento onwheel se dispara directamente por la acción del usuario con la rueda del ratón o trackpad, proporcionando datos detallados (como la dirección y magnitud) antes de que el desplazamiento ocurra.
¿Por qué mi página se sigue desplazando cuando uso `onwheel` para otra cosa?
Esto ocurre porque el navegador sigue ejecutando la acción de scroll por defecto. Para evitarlo, debes llamar a event.preventDefault() al principio de tu función manejadora del evento onwheel. Esto le indica al navegador que no realice su acción predeterminada.
¿El evento `onwheel` funciona en dispositivos táctiles?
No. El evento onwheel está diseñado para dispositivos de entrada con rueda (ratones, trackpads). Para interacciones táctiles de deslizamiento, debes utilizar los eventos táctiles como touchstart, touchmove y touchend.
¿Qué es el "throttling" y por qué es importante para los eventos de scroll?
El throttling es una técnica de programación que limita la frecuencia de ejecución de una función. Es crucial para los eventos de scroll porque se disparan muy rápidamente. Aplicar throttling a tu manejador de eventos previene que se ejecute código pesado en exceso, lo que evita caídas de rendimiento y mantiene la interfaz de usuario fluida.
Si quieres conocer otros artículos parecidos a Dominando el Scroll en JavaScript: Guía Completa puedes visitar la categoría Juegos.
