Command Palette

Search for a command to run...

Passive Event Listeners: Mejora el Rendimiento de Eventos

Aprende a usar passive event listeners para optimizar el rendimiento de scroll, touch y otros eventos, eliminando bloqueos innecesarios en el hilo principal.

Lectura: 14 min
Nivel: Intermedio

TL;DR - Resumen rápido

  • Los passive event listeners indican al navegador que el listener no llamará a preventDefault()
  • Mejoran significativamente el rendimiento de scroll y touch en dispositivos móviles
  • Se activan pasando { passive: true } como tercer parámetro a addEventListener()
  • Son especialmente útiles para eventos de scroll, touch y wheel
  • El navegador puede ejecutarlos de forma asíncrona sin bloquear el renderizado

Introducción a Passive Event Listeners

Los passive event listeners son una característica introducida en 2016 para mejorar el rendimiento de ciertos eventos, especialmente scroll y touch en dispositivos móviles. Fueron creados como respuesta al problema de scroll lento causado por listeners que bloqueaban el renderizado. Cuando agregas un listener con la opción { passive: true }, le estás diciendo al navegador que este listener nunca llamará a preventDefault(), lo cual permite al navegador optimizar la ejecución del evento.

Esta optimización es crítica porque eventos como touchstart y wheel pueden bloquear el renderizado si el listener tarda en ejecutarse. Con passive listeners, el navegador puede ejecutar el listener de forma asíncrona mientras continúa con el scroll o la animación, resultando en una experiencia de usuario mucho más fluida, especialmente en dispositivos con pantallas táctiles.

  • <strong>Mejora de rendimiento</strong>: El navegador puede ejecutar listeners sin bloquear el renderizado
  • <strong>Scroll fluido</strong>: Elimina el retraso en scroll y touch en dispositivos móviles
  • <strong>Optimización automática</strong>: El navegador decide cuándo ejecutar el listener
  • <strong>Compatibilidad</strong>: Soportado en todos los navegadores modernos
  • <strong>Uso simple</strong>: Solo requiere agregar { passive: true } a addEventListener()

¿Qué son los Passive Event Listeners?

Un passive event listener es un listener que le promete al navegador que nunca llamará a preventDefault(). Esta promesa permite al navegador optimizar la ejecución del evento porque sabe que no necesita esperar a que el listener termine para continuar con el comportamiento por defecto del evento.

Sintaxis Básica

Para crear un passive listener, pasas un objeto de opciones como tercer parámetro a addEventListener() con la propiedad passive establecida en true. Esta opción reemplaza el parámetro booleano useCapture que se usaba anteriormente. Siempre usa{ passive: true } para eventos de scroll (wheel, touchstart, touchmove) cuando no necesitas prevenir el comportamiento por defecto, ya que esto mejora significativamente el rendimiento en dispositivos móviles.

passive-basico.js
Loading code...

Este ejemplo muestra la diferencia entre un listener normal y un passive listener. El listener normal puede llamar a preventDefault(), mientras que el passive listener no puede. Si intentas llamar a preventDefault() en un passive listener, el navegador lanzará una advertencia en la consola.

Compatibilidad y Feature Detection

Los passive event listeners son soportados en todos los navegadores modernos, pero si necesitas soportar navegadores más antiguos, puedes usar feature detection para verificar si la opción passive es soportada antes de usarla.

feature-detection.js
Loading code...

Este ejemplo muestra cómo usar feature detection para verificar si la opción passive es soportada. Si no lo es, el código usa el parámetro booleano useCapture como fallback. Esto asegura que tu código funcione en navegadores antiguos mientras aprovecha la optimización en navegadores modernos.

Cómo Funcionan los Passive Listeners

Para entender cómo funcionan los passive listeners, primero necesitas entender el problema que resuelven. Cuando un evento se dispara, el navegador debe esperar a que todos los listeners se ejecuten antes de decidir si continuar con el comportamiento por defecto. Si un listener tarda en ejecutarse, esto bloquea el renderizado y causa una percepción de lentitud.

El Problema del Bloqueo de Renderizado

Sin passive listeners, el navegador debe esperar a que todos los listeners se ejecuten antes de continuar con el comportamiento por defecto. Esto es necesario porque un listener podría llamar a preventDefault(), lo cual cancelaría el comportamiento por defecto. Sin embargo, esto causa problemas de rendimiento, especialmente en dispositivos móviles.

bloqueo-renderizado.js
Loading code...

Este ejemplo muestra cómo un listener lento puede bloquear el renderizado. El listener simula una operación costosa que tarda 100ms en completarse. Durante este tiempo, el navegador no puede continuar con el scroll, lo cual causa una percepción de lentitud en la interfaz.

Advertencia: Listeners lentos en eventos de scroll

Los listeners lentos en eventos de scroll (wheel, touchstart, touchmove) pueden causar una experiencia de usuario terrible en dispositivos móviles. Siempre optimiza estos listeners o usa { passive: true } cuando sea posible.

La Solución: Passive Listeners

Con passive listeners, el navegador sabe que el listener nunca llamará a preventDefault(), por lo que puede ejecutar el listener de forma asíncrona mientras continúa con el comportamiento por defecto. Esto elimina el bloqueo del renderizado y mejora significativamente el rendimiento.

solucion-passive.js
Loading code...

Este ejemplo muestra cómo el mismo listener lento no bloquea el renderizado cuando se usa { passive: true }. El navegador puede continuar con el scroll mientras el listener se ejecuta en segundo plano, resultando en una experiencia de usuario mucho más fluida.

Ejecución asíncrona de passive listeners

Los passive listeners se ejecutan de forma asíncrona, lo cual significa que el navegador puede decidir cuándo ejecutarlos. Esto permite al navegador priorizar el renderizado y la respuesta al usuario sobre la ejecución del listener.

Cuándo Usar Passive Listeners

Los passive listeners son especialmente útiles para eventos que se disparan con alta frecuencia y que pueden causar problemas de rendimiento si bloquean el renderizado. Sin embargo, no siempre son apropiados. Debes usarlos cuando no necesitas prevenir el comportamiento por defecto del evento.

Eventos Recomendados para Passive Listeners

Algunos eventos son ideales para passive listeners porque se disparan con alta frecuencia y raramente necesitan prevenir el comportamiento por defecto. Siempre usa { passive: true }por defecto en eventos de scroll (wheel, touchstart, touchmove) a menos que específicamente necesites prevenir el comportamiento por defecto. Esto mejora el rendimiento sin afectar la funcionalidad y es especialmente crítico para la experiencia de usuario en dispositivos móviles.

eventos-recomendados.js
Loading code...

Este ejemplo muestra cómo usar passive listeners para los eventos más comunes que se benefician de esta optimización. El evento wheel se usa para trackpads y ruedas del mouse, touchstart y touchmove para interacciones táctiles, y scroll para el scroll del documento.

Cuándo NO Usar Passive Listeners

No debes usar passive listeners cuando necesitas prevenir el comportamiento por defecto del evento. Si marcas un listener como passive pero intentas llamar a preventDefault(), el navegador lanzará una advertencia en la consola y el comportamiento por defecto no será prevenido.

cuando-no-usar.js
Loading code...

Este ejemplo muestra un caso donde NO debes usar passive listeners. El evento submit necesita prevenir el comportamiento por defecto para evitar que el formulario se envíe de forma tradicional. Si usas { passive: true }, preventDefault() no funcionará y el navegador lanzará una advertencia.

Advertencia: preventDefault() en passive listeners

Si intentas llamar a preventDefault() en un passive listener, el navegador lanzará una advertencia en la consola: "Unable to preventDefault inside passive event listener invocation". El comportamiento por defecto no será prevenido.

Ejemplos Prácticos

Ahora que entiendes la teoría, veamos algunos ejemplos prácticos de cómo usar passive listeners en situaciones reales. Estos ejemplos demuestran cómo mejorar el rendimiento de aplicaciones web comunes.

Lazy Loading con Passive Listeners

El lazy loading es una técnica común para cargar contenido a medida que el usuario hace scroll. Usar passive listeners para detectar el scroll mejora significativamente el rendimiento, especialmente en dispositivos móviles. Cuando usas passive listeners para eventos de alta frecuencia como scroll, considera combinarlos con debounce o throttle para limitar la frecuencia de ejecución, maximizando los beneficios de rendimiento.

lazy-loading.js
Loading code...

Este ejemplo muestra cómo implementar lazy loading usando passive listeners. El listener de scroll detecta cuando el usuario se acerca al final de la página y carga más contenido. Al usar { passive: true }, el scroll permanece fluido incluso mientras se carga el contenido adicional.

Tracking de Interacciones con Passive Listeners

El tracking de interacciones del usuario (scroll depth, clicks, etc.) es una práctica común en analítica web. Usar passive listeners para estos eventos asegura que el tracking no afecte el rendimiento de la aplicación.

tracking-interacciones.js
Loading code...

Este ejemplo muestra cómo implementar tracking de scroll depth usando passive listeners. El listener detecta cuánto ha scrolleado el usuario y envía esta información a un servicio de analítica. Al usar { passive: true }, el tracking no afecta la fluidez del scroll.

Errores Comunes

Estos son los errores más frecuentes que encontrarás al trabajar con passive event listeners, especialmente cuando estás aprendiendo o migrando código legacy.

Error: preventDefault() en Passive Listener

El error más común es intentar llamar a preventDefault() en un passive listener. Como el navegador sabe que el listener es passive, ignora preventDefault() y lanza una advertencia en la consola.

error-preventdefault.js
Loading code...

El código incorrecto usa { passive: true } pero intenta llamar a preventDefault(). El navegador lanza una advertencia y el comportamiento por defecto no es prevenido. La solución es quitar la opción passive o no llamar a preventDefault().

Error: Usar Passive Siempre

Otro error común es usar passive listeners para todos los eventos sin considerar si realmente son apropiados. Los passive listeners solo mejoran el rendimiento para ciertos eventos y pueden causar problemas si se usan incorrectamente.

error-siempre-passive.js
Loading code...

El código incorrecto usa passive listeners para todos los eventos, incluso para submit donde necesita prevenir el comportamiento por defecto. La solución es usar passive solo para eventos apropiados (scroll, touch, wheel) y no usarlo cuando necesitas preventDefault().

Error: No Verificar Compatibilidad

Si necesitas soportar navegadores antiguos, debes verificar si la opción passive es soportada antes de usarla. Si no lo haces, el código puede fallar en navegadores que no soportan esta característica.

error-compatibilidad.js
Loading code...

El código incorrecto usa { passive: true } sin verificar si es soportado. En navegadores antiguos, esto puede causar errores o comportamientos inesperados. La solución es usar feature detection para verificar la compatibilidad antes de usar passive listeners.

Resumen: Passive Event Listeners

Conceptos principales:

  • Los passive listeners prometen no llamar a preventDefault()
  • Mejoran el rendimiento de scroll y touch en móviles
  • Se activan con { passive: true } en addEventListener()
  • El navegador puede ejecutarlos de forma asíncrona
  • No bloquean el renderizado ni la respuesta al usuario

Mejores prácticas:

  • Usa passive para wheel, touchstart y touchmove por defecto
  • No uses passive cuando necesites preventDefault()
  • Verifica compatibilidad en navegadores antiguos
  • Combina passive con debounce/throttle para scroll
  • Usa feature detection para soporte cross-browser