CodeForge

Maquetación Web Dinámica / JavaScript para maquetadores: darle vida al DOM

Delegación de eventos: un listener para muchos

Teoría20 min20 XP

Tienes una lista de 100 elementos, cada uno con un botón de borrar. ¿Pones 100 listeners? ¿Y qué pasa cuando agregas el elemento 101 — le pones otro a mano? Hay una forma mucho mejor: UN solo listener en el padre que atiende a todos sus hijos, presentes y futuros. Se llama delegación de eventos, y es uno de esos trucos que, una vez lo entiendes, usas para siempre.

El burbujeo: el evento sube por el árbol

El patrón: escuchar en el padre, identificar con closest

delegacion.js
// UN listener en el contenedor padre, no en cada hijo
lista.addEventListener("click", (event) => {
// ¿el clic salió de un botón .borrar (o algo dentro de él)?
const boton = event.target.closest(".borrar");
if (!boton) return;                 // el clic fue en otra parte: ignora
boton.closest("li").remove();       // borra el <li> que lo contiene
});

La pieza clave es event.target.closest(selector): sube desde el elemento clicado buscando el ancestro más cercano que coincida con el selector (incluido él mismo). Resuelve el problema de "el usuario pudo hacer clic en el icono DENTRO del botón, no en el botón exacto" — closest encuentra el botón de todos modos. Si no hay coincidencia, devuelve null y el return ignora el clic.

La magia: funciona con elementos futuros

Aquí está lo poderoso: agrega tareas nuevas con el botón y bórralas — SE BORRAN, aunque nunca les pusiste un listener. Como el único listener vive en el <ul> padre y los clics de los hijos nuevos también burbujean hasta él, funcionan automáticamente. Con listeners individuales tendrías que acordarte de añadir uno a cada elemento creado; con delegación, el padre ya los cubre a todos.

Tienes una lista donde el usuario AGREGA elementos dinámicamente. Le pusiste un listener a cada botón con querySelectorAll en la carga. Los botones viejos funcionan, los nuevos no. ¿Por qué, y la solución?

Mini-reto

Extiende el ejemplo: además de borrar, haz que un clic sobre el TEXTO de una tarea (el <span>) le ponga una clase .hecha que la tache (usa event.target.closest("span") o revisa event.target.tagName). Todo con el MISMO listener del <ul>: dentro, decides qué hacer según de dónde salió el clic. Un solo listener, varias acciones — la delegación en su máxima expresión.

Qué sigue

Ya borras y agregas elementos sueltos. La próxima lección formaliza eso en un patrón central de las apps modernas: RENDERIZAR desde datos. Tienes un array de objetos (noticias, productos, tareas) y generas el HTML de la lista a partir de él — el puente entre "tengo datos" y "se ven en pantalla".