Event listeners en JavaScript: click, submit, scroll y delegación
Event listeners en JavaScript
Event listeners en JavaScript: click, submit, scroll | We.Inc
Mira cómo se disparan los eventos en un registro en vivo y copia patrones de addEventListener para click, submit, scroll, delegación, once, passive y limpieza con AbortController.
Un event listener es una función que el navegador ejecuta cuando algo le ocurre a un elemento, como un clic, el envío de un formulario o un desplazamiento. Se añade con element.addEventListener(type, handler, options). El objeto de opciones activa once, passive, capture o una señal de cancelación, y removeEventListener quita de nuevo esa misma función más adelante.
Opciones de addEventListener
Opción
Valor por defecto
Efecto
Elimina el listener automáticamente tras su primera llamada
false (true para wheel y touch en window, document y body)
Promete no llamar a preventDefault, así el desplazamiento nunca espera
Se ejecuta en el camino de bajada hacia el objetivo en lugar de en el de subida
Un AbortSignal; al cancelarlo se elimina el listener
Los eventos se entienden mejor cuando puedes verlos. El registro de eventos de abajo anota cada llamada a un listener en una pequeña zona de pruebas, así que puedes hacer clic, escribir, enviar y desplazarte y observar el orden, el objetivo y el efecto de cada opción. Después copia los patrones para las tareas que realmente tengas.
Usa los controles de la zona de pruebas: haz clic en los botones, escribe en el campo, envía el formulario y desplaza la caja.
Cada llamada a un listener aparece en el registro con su tipo de evento, el elemento que lo originó y el elemento que lo gestionó.
Activa once para ver cómo un listener desaparece tras una llamada, o desactiva la delegación para ver cómo la lista gestiona los clics elemento por elemento.
Copia cualquiera de los ejemplos de abajo. Todos son JavaScript puro, sin librerías.
Pasa una función con nombre cuando vayas a eliminar el listener. Una arrow function en línea no se puede identificar después.
AbortController elimina un grupo entero de listeners con una sola llamada a abort(), algo ideal para componentes que se montan y desmontan.
Escucha submit en el formulario, no click en el botón, para que también se gestione pulsar Enter en un campo.
Usa event.target.closest("selector") en los handlers delegados. Un clic en un icono dentro de un botón sigue encontrando el botón.
focus y blur no hacen bubbling. Delega focusin y focusout en su lugar.
Preguntas frecuentes
¿Qué es la delegación de eventos?
En lugar de añadir un listener a cada elemento, añades uno a un padre común. Como la mayoría de los eventos suben (bubbling), el padre recibe los clics de todos sus hijos, incluidos los añadidos después, y usa event.target para saber en cuál se hizo clic.
¿Por qué removeEventListener no elimina mi listener?
Debe recibir la misma referencia de función y el mismo valor de capture que recibió addEventListener. Dos arrow functions con código idéntico siguen siendo funciones distintas, así que define el handler una vez y pasa esa variable en ambas llamadas.
¿Qué hace passive: true?
Le dice al navegador que tu handler no llamará a preventDefault, así la página puede empezar a desplazarse de inmediato en lugar de esperar a que termine tu código. Los navegadores ya usan passive por defecto en los listeners de wheel y touch sobre window, document y body.
¿Cuál es la diferencia entre target y currentTarget?
target es el elemento más interno donde empezó el evento, y no cambia mientras el evento sube. currentTarget es el elemento cuyo listener se está ejecutando en ese momento, y cambia en cada nivel.
¿form.submit() dispara el evento submit?
No. Llamar a form.submit() desde un script se salta el evento submit y la validación. Usa form.requestSubmit() cuando quieras que se ejecuten tus listeners de submit y las comprobaciones integradas.
¿addEventListener u onclick?
La propiedad onclick guarda una sola función, así que asignar una segunda reemplaza la primera. addEventListener permite que varios listeners independientes compartan un elemento y admite el objeto de opciones, por eso es la mejor opción por defecto.
Cómo viaja un evento
Un evento disparado se mueve en tres fases. Baja desde window hasta el objetivo en la fase de captura, llega al objetivo y luego vuelve a subir por cada ancestro en la fase de bubbling. Los listeners se ejecutan en la fase de bubbling salvo que pidas capture.
stopPropagation detiene el recorrido, así que los ancestros nunca lo reciben. Úsalo poco, porque los listeners delegados y la analítica situados más arriba en el árbol dejan de funcionar sin avisar.
Desplazamiento sin ralentizar la página
Los eventos scroll pueden dispararse muchas veces por cada fotograma de movimiento. Mantén el handler mínimo, lee posiciones en lugar de modificar el layout y agrupa las actualizaciones visuales dentro de requestAnimationFrame.
Para tareas como mostrar elementos cuando entran en pantalla, un IntersectionObserver suele ser más barato que medir en cada desplazamiento. El evento scrollend, Baseline desde diciembre de 2025 según MDN, se dispara una vez cuando el movimiento se detiene.
Limpiar los listeners
Los listeners en objetos de larga vida como window o document mantienen vivo su handler y todo lo que este referencia. Cuando desaparezca la parte de la página que los necesitaba, elimínalos.
La opción signal lo deja todo ordenado. Crea un AbortController por funcionalidad, pasa su signal a cada listener y ejecuta abort() una vez al desmontar.
Los tipos de evento que más usarás
Eventos comunes y dónde escucharlos
Evento
Se dispara cuando
¿Hace bubbling?
Se pulsa y suelta un puntero, o se pulsa Enter o Space sobre un botón
Sí
El valor de un campo cambia mientras el visitante escribe
Sí
Se confirma el valor de un campo, por ejemplo al perder el foco o al seleccionar
Sí
Se envía un formulario; se dispara en el elemento form
Sí
Se pulsa una tecla
Sí
focus / blur
Un elemento gana o pierde el foco
No
focusin / focusout
Igual que focus y blur, pero con bubbling
Sí
DOMContentLoaded
El HTML se ha analizado y los scripts diferidos se han ejecutado; escúchalo en document
No aplica
Patrones para copiar
Click con un handler con nombre que puedes eliminar
Guardar la función en una variable es lo que hace que removeEventListener funcione.
Gestionar el envío de un formulario sin recargar
preventDefault detiene la navegación propia del navegador. event.submitter te indica qué botón de envío se usó.
Delegación de eventos en una lista
Un solo listener en la lista gestiona todos los botones de borrar, incluidas las filas añadidas después de cargar la página.
Un listener de scroll passive limitado a una actualización por fotograma
passive: true permite que el desplazamiento empiece enseguida, y requestAnimationFrame limita el trabajo a una vez por fotograma.
once y AbortController
El primer listener se ejecuta una sola vez. Los demás se eliminan todos juntos con una sola llamada a abort().
Atajo de teclado que ignora lo que se escribe en los campos
Comprobar el target evita que el atajo se dispare mientras alguien escribe una barra en un cuadro de búsqueda.
Manzanas
Pan
Café
click en el botón: target {target}
(once: ya eliminado)
el click llegó a la zona de pruebas (fase {phase}): target {target}, currentTarget {current}
input: el valor ahora es "{value}"
change: valor confirmado
submit en el formulario, submitter {submitter}; preventDefault evitó la recarga
scroll (passive, uno por fotograma): scrollTop {n}
click delegado en la lista: elemento {item}
listener por elemento: {item}
Registro de eventos en vivo
Haz clic, escribe, envía y desplázate en la zona de pruebas. Cada llamada a un listener se anota en el registro con su tipo, target y currentTarget.