Delegación de eventos en JavaScript (Con ejemplos)
La delegación de eventos en JavaScript es una técnica clave para crear interfaces más eficientes y escalables. En este artículo vas a ver ejemplos claros, entender cómo funciona internamente y aprender cómo se hace de forma correcta y profesional. Descubrí por qué se utiliza en proyectos reales y cómo puede simplificar tu código al manejar eventos dinámicos.

Índice
1. ¿Qué es la delegación de eventos en JavaScript?
La delegación de eventos (Event Delegation) es una técnica en JavaScript que consiste en asignar un único listener (colocación del método addEventListener()) a un elemento contenedor para manejar eventos que ocurren en sus elementos hijos, en lugar de agregar un listener a cada hijo individual.
Esto es posible gracias al burbujeo de eventos (event bubbling): cuando ocurre un evento en un elemento hijo (por ejemplo, un click), el evento sube por el DOM hasta sus elementos ancestros, permitiendo que el contenedor lo capture y lo gestione.
1.1. Ejemplo de funcionamiento:
Supongamos que queremos una lista de tareas donde podamos agregar elementos nuevos.
Así lo haríamos con la delegación de eventos:
Lista HTML del ejemplo:
<ul id="lista">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>Lenguaje del código: HTML, XML (xml)
JavaScript que cumple el principio de delegación de eventos:
const lista = document.getElementById('lista');
lista.addEventListener('click', (event) => {
if (event.target.tagName === 'LI') {
console.log('Click en:', event.target.textContent);
}
});Lenguaje del código: JavaScript (javascript)
Aquí:
- El listener está en
<ul> - El
clickocurre en un<li> - El evento burbujea hasta el
<ul> - Se identifica el elemento real con
event.target.
¿Y SIN LA DELEGACIÓN DE EVENTOS? Listener directo en cada elemento
<ul>
<li class="item">Item 1</li>
<li class="item">Item 2</li>
<li class="item">Item 3</li>
</ul>
Lenguaje del código: HTML, XML (xml)
const items = document.querySelectorAll('.item');
items.forEach(item => {
item.addEventListener('click', () => {
console.log('Click en:', item.textContent);
});
});Lenguaje del código: JavaScript (javascript)
Qué está pasando:
- Se agrega un listener por cada
<li> - Cada elemento maneja su propio evento
- Funciona bien para pocos elementos estáticos
Problemas de este enfoque:
- Escalabilidad pobre
Si hay 100 o 1.000 elementos → 100 o 1.000 listeners. - Elementos dinámicos no funcionan
Si agregás un<li>después, no tiene listener. - Mayor consumo de memoria
Cada listener es una función en memoria.
1.2. Beneficios de la delegación de eventos:
- Evita muchos listeners innecesarios
- Mejora performance y consumo de memoria
- Permite manejar elementos creados dinámicamente
- Hace el código más mantenible y escalable
1.3. Cuándo usarla:
- Listas, tablas, menús
- Botones o ítems generados dinámicamente
- Interfaces con muchos elementos interactivos
En resumen, la delegación de eventos es una estrategia fundamental para trabajar profesionalmente con el DOM, especialmente en aplicaciones dinámicas y reales.
2. ¿Cómo crear la delegación de eventos? Paso a paso.
- Elegir un contenedor padre adecuado y estable en el DOM.
- Registrar un solo addEventListener() en ese contenedor.
- Asegurarse de que el evento utilizado burbujee.
- Detectar el elemento origen con event.target.
- Filtrar correctamente el objetivo usando matches() o closest().
- Ejecutar la lógica solo para los elementos deseados.
- Probar la delegación con elementos dinámicos creados posteriormente.
- Mantener el listener simple, claro y fácil de mantener.
2.1. Elegir un contenedor padre adecuado y estable en el DOM.
¿Qué significa “contenedor padre adecuado y estable”?
Elegir un contenedor adecuado implica seleccionar un elemento del DOM que:
- Contenga realmente a los elementos que van a disparar el evento
- No se elimine ni se reemplace frecuentemente
- Sea lo más cercano posible a los elementos objetivo
- Permita que el burbujeo funcione de forma natural
👉 No es “cualquier padre”, es el padre correcto.
Registrar un solo addEventListener() en el contenedor implica centralizar el manejo de eventos en un único punto del DOM, permitiendo gestionar múltiples interacciones de elementos hijos mediante el burbujeo de eventos.
2.1.1. Ejemplo incorrecto: contenedor demasiado general
❌ Código
<body>
<ul id="lista">
<li><button>Eliminar</button></li>
<li><button>Eliminar</button></li>
</ul>
</body>Lenguaje del código: HTML, XML (xml)
document.body.addEventListener('click', (event) => {
if (event.target.tagName === 'BUTTON') {
console.log('Botón eliminado');
}
});Lenguaje del código: JavaScript (javascript)
❌ Problemas
bodyrecibe todos los clicks de la página- Se ejecuta lógica innecesaria
- Riesgo de colisiones con otros botones
- Peor rendimiento y menor claridad
📌 Conclusión: el contenedor es demasiado amplio.
2.1.2. Ejemplo correcto: contenedor específico y cercano
✅ Código
<ul id="lista">
<li><button class="btn-eliminar">Eliminar</button></li>
<li><button class="btn-eliminar">Eliminar</button></li>
</ul>
Lenguaje del código: HTML, XML (xml)
const lista = document.getElementById('lista');
lista.addEventListener('click', (event) => {
if (event.target.matches('.btn-eliminar')) {
console.log('Elemento eliminado');
}
});
Lenguaje del código: JavaScript (javascript)
✅ Ventajas
- El listener solo escucha eventos relevantes
- Menor propagación innecesaria
- Código más claro y mantenible
- Mejor rendimiento
📌 Regla profesional: Delegá eventos en el ancestro más cercano común a los elementos objetivo.
2.1.3. Contenedor estable: ¿por qué es importante?
Un contenedor estable es aquel que:
- No se elimina del DOM
- No se reemplaza con
innerHTML - Vive durante todo el ciclo de vida del componente o página.
2.2. Registrar un solo addEventListener() en ese contenedor.
¿Qué significa “registrar un solo addEventListener()”?
Significa que:
- No se agrega un listener por cada elemento hijo
- Se agrega un único
addEventListener()en el contenedor padre - Ese listener se encarga de todos los eventos de los hijos relevantes
📌 Esto es la esencia de la delegación de eventos.
2.2.1. Ejemplo incorrecto: múltiples addEventListener()
❌ Código
<ul id="lista">
<li><button class="btn">Editar</button></li>
<li><button class="btn">Editar</button></li>
<li><button class="btn">Editar</button></li>
</ul>Lenguaje del código: HTML, XML (xml)
const botones = document.querySelectorAll('.btn');
botones.forEach(boton => {
boton.addEventListener('click', () => {
console.log('Editar elemento');
});
});Lenguaje del código: JavaScript (javascript)
❌ Problemas
- Se crean muchos listeners
- Difícil de mantener
- Si se agregan nuevos botones → no funcionan
- Mayor consumo de memoria
📌 Esto NO es delegación de eventos.
2.2.2. Ejemplo correcto: un solo addEventListener()
✅ Código
const lista = document.getElementById('lista');
lista.addEventListener('click', (event) => {
if (event.target.matches('.btn')) {
console.log('Editar elemento');
}
});Lenguaje del código: JavaScript (javascript)
✅ Qué está pasando
- El click ocurre en un botón
- El evento burbujea hasta
#lista - El listener del contenedor se ejecuta
- Se filtra el objetivo del evento
- Se ejecuta la lógica correcta
📌 Un solo listener maneja todos los botones.
2.3. Asegurarse de que el evento utilizado burbujee.
¿Qué significa que un evento “burbujee”?
Que un evento se propaga desde el elemento donde ocurre hacia sus elementos padres, siguiendo esta secuencia:
Elemento hijo → padre → abuelo → document → windoLenguaje del código: JavaScript (javascript)
A esto se le llama event bubbling (burbujeo de eventos).
📌 La delegación de eventos depende 100% del burbujeo:
si el evento no burbujea, el contenedor nunca lo recibe.
2.3.1. Ejemplo visual del burbujeo
HTML
<div id="contenedor">
<button id="boton">Click</button>
</div>
Lenguaje del código: HTML, XML (xml)
JavaScript
const contenedor = document.getElementById('contenedor');
contenedor.addEventListener('click', () => {
console.log('Click capturado en el contenedor');
});
Lenguaje del código: JavaScript (javascript)
¿Qué pasa?
- El usuario hace click en el botón
- El evento
clickse dispara en#boton - El evento burbujea hasta
#contenedor - El listener del contenedor se ejecuta
👉 Esto confirma que click burbujea.
2.3.2. Eventos que sí burbujean (aptos para delegación)
Algunos tipos de eventos comunes que burbujean:
clickdblclickmousedown,mouseupkeydown,keyupinputchangesubmit
📌 Estos son los más usados en delegación de eventos.
2.3.3. Eventos que NO burbujean (problema común)
Eventos que NO burbujean:
focusblurmouseentermouseleaveloadscroll(en muchos contextos)
2.4. Detectar el elemento origen con event.target.
¿Qué es event.target?
En JavaScript, cada vez que ocurre un evento (un clic, una tecla presionada, etc.), se crea un objeto llamado Event que contiene toda la información de lo ocurrido. La propiedad .target es una referencia al elemento exacto que originó el evento.
La diferencia vital: target vs. currentTarget
Para ser un experto, debes conocer esta distinción:
event.target: Es el «invitado que golpeó la puerta» (el elemento hijo donde se hizo clic).event.currentTarget: Es el «dueño de casa» (el elemento padre que tiene eladdEventListener).
2.4.1. Ejemplo en código: Identificando el origen
Imagina un tablero con tres botones de diferentes colores. En lugar de poner 3 listeners, pondremos uno en el contenedor.
HTML
<div id="tablero" style="padding: 20px; background: #f0f0f0;">
<button class="btn-rojo">Rojo</button>
<button class="btn-azul">Azul</button>
<button class="btn-verde">Verde</button>
<p>Haz clic en cualquier botón o en el fondo gris.</p>
</div>Lenguaje del código: HTML, XML (xml)
JavaScript
const tablero = document.querySelector('#tablero');
tablero.addEventListener('click', (event) => {
// Capturamos el elemento que originó el clic
const elementoOrigen = event.target;
console.log("Hiciste clic en:", elementoOrigen.tagName);
// Podemos leer cualquier propiedad del elemento origen
if (elementoOrigen.className === 'btn-rojo') {
alert("¡Peligro! Botón rojo presionado.");
}
if (elementoOrigen.id === 'tablero') {
console.log("Hiciste clic en el fondo gris del contenedor.");
}
});Lenguaje del código: JavaScript (javascript)
Explicación del código:
event.targetnos dice exactamente qué tocó el usuario.- Si el usuario toca el botón rojo,
event.targetserá ese botón. - Si el usuario toca el espacio vacío gris,
event.targetserá eldiv#tablero. - Esto nos permite tomar decisiones basadas en quién disparó la acción.
2.4.2. El «Problema» de los elementos anidados
Aquí es donde muchos estudiantes se confunden. ¿Qué pasa si el botón tiene un icono dentro?
HTML
<button class="btn-borrar">
<i class="icono-basura">🗑️</i> Borrar Item
</button>Lenguaje del código: HTML, XML (xml)
Si el usuario hace clic exactamente sobre el icono, el event.target será el <i>, ¡no el botón! Esto puede romper tu lógica si solo buscas la clase del botón.
EJEMPLO del problema:
JavaScript
tablero.addEventListener('click', (e) => {
// Si hago clic en el emoji 🗑️, e.target es el <i>
// Por lo tanto, e.target.classList.contains('btn-borrar') será FALSE.
if (e.target.classList.contains('btn-borrar')) {
console.log("Borrado"); // ¡No se ejecutaría!
}
});Lenguaje del código: JavaScript (javascript)
¿Cuál es el siguiente paso?
Para solucionar este «problema» de los clics en elementos hijos, necesitamos aprender a Filtrar Objetivos (siguiente paso).
2.5. Filtrar correctamente el objetivo usando matches() o closest().
¿Por qué es necesario filtrar el objetivo?
Como vimos en el paso anterior, event.target es el elemento exacto donde ocurrió el clic. En el desarrollo moderno, los botones casi nunca son solo texto; tienen iconos, spans o incluso capas de diseño.
El problema del «clic fallido»:
Si tienes un botón de «Eliminar» con un icono de basura adentro:
- Si el usuario toca el borde del botón $\rightarrow$
event.targetes el<button>. (Funciona) - Si el usuario toca el icono $\rightarrow$
event.targetes el<i>. (¡Tu código falla si solo buscas el botón!)
Necesitamos una forma de decir: «No me importa si tocaste el icono o el texto, búscame el botón que los contiene». Aquí es donde entran en juego los métodos matches() o closest()
2.5.1. ¿Qué es matches()?
matches() es un método de los elementos del DOM que:
👉 Devuelve true si el elemento coincide con un selector CSS.
Sintaxis
element.matches(selectorCSS)Lenguaje del código: CSS (css)
EJEMPLO simple con matches()
HTML
<ul id="menu">
<li><a href="#" class="link">Inicio</a></li>
<li><a href="#" class="link">Contacto</a></li>
</ul>
Lenguaje del código: HTML, XML (xml)
JavaScript
const menu = document.getElementById('menu');
menu.addEventListener('click', (event) => {
if (event.target.matches('.link')) {
console.log('Link clickeado');
}
});
Lenguaje del código: JavaScript (javascript)
Explicación
- El click ocurre en un
<a> event.targetes ese<a>matches('.link')verifica si es un link válido- Se ejecuta la lógica
📌 Ideal cuando el evento ocurre directamente sobre el elemento objetivo.
PROBLEMA común con matches()
HTML
<button class="btn">
<span>Guardar</span>
</button>Lenguaje del código: HTML, XML (xml)
Si el usuario hace click en el <span>:
event.target→<span>event.target.matches('.btn')→ ❌false
👉 El evento no se detecta.
2.5.2. ¿Qué es closest()?
closest() es un método que:
👉 Busca desde el elemento actual hacia arriba en el DOM
👉 Devuelve el primer ancestro que coincida con el selector
Sintaxis
element.closest(selectorCSS)Lenguaje del código: CSS (css)
EJEMPLO profesional con closest()
HTML
<ul id="lista">
<li>
<button class="btn-eliminar">
<span>Eliminar</span>
</button>
</li>
</ul>
Lenguaje del código: HTML, XML (xml)
JavaScript
const lista = document.getElementById('lista');
lista.addEventListener('click', (event) => {
const boton = event.target.closest('.btn-eliminar');
if (boton && lista.contains(boton)) {
console.log('Eliminar elemento');
}
});
Lenguaje del código: JavaScript (javascript)
Explicación
- El click puede ser en
<span>o<button> closest('.btn-eliminar')sube hasta el botón- Se valida que pertenezca al contenedor
- Se ejecuta la acción
📌 Esto es delegación robusta.
2.5.3. ¿Cuándo usar matches() y cuándo closest()?
Usá matches() cuando:
- El evento ocurre directamente en el elemento
- El HTML es simple
- No hay elementos internos clickeables
Usá closest() cuando:
- Hay elementos anidados (
span,img,svg) - El markup puede cambiar
- Querés mayor tolerancia a clicks internos
📌 En proyectos reales, closest() es el más usado.
2.5.4. Buenas prácticas
✔ Preferir closest() en componentes complejos
✔ Verificar siempre que el elemento pertenezca al contenedor
✔ Usar selectores específicos
✔ Evitar depender de la estructura interna del HTML
2.6. Ejecutar la lógica solo para los elementos deseados.
¿Qué significa «Ejecutar la lógica solo para los elementos deseados»?
Como ya sabes, cuando pones un addEventListener en un padre (por ejemplo, un div), ese padre va a escuchar absolutamente todo lo que pase dentro de él: clics en el fondo, clics en el texto decorativo, clics en los bordes, etc.
Si no filtras, tu código intentará ejecutar la lógica (como borrar una base de datos o cambiar de página) cada vez que el usuario toque cualquier pixel del contenedor. Filtrar es poner un «guardia de seguridad» al principio de tu función para que solo deje pasar a los elementos que realmente nos interesan.
2.6.1. Ejemplo práctico: Un panel de control con múltiples acciones
Imagina que tienes una lista de usuarios y cada fila tiene dos botones: uno para Editar y otro para Eliminar. Queremos que la lógica sea distinta para cada uno y que no pase nada si el usuario hace clic en el nombre del usuario.
HTML
<ul id="lista-usuarios">
<li class="usuario-item">
<span>Juan Pérez</span>
<button class="btn-accion" data-tipo="editar">Editar</button>
<button class="btn-accion" data-tipo="eliminar">Eliminar</button>
</li>
</ul>
Lenguaje del código: HTML, XML (xml)
JavaScript
const lista = document.querySelector('#lista-usuarios');
lista.addEventListener('click', (event) => {
// 1. EL FILTRO INICIAL (The Guard Clause)
// Buscamos el botón más cercano. Si el clic no fue en un botón, será null.
const boton = event.target.closest('.btn-accion');
// 2. LA VALIDACIÓN: Si no es un botón de acción, salimos de la función inmediatamente.
if (!boton) return;
// 3. LÓGICA POR PROPÓSITO: Ahora que sabemos que es un botón,
// identificamos CUÁL de ellos es usando data-attributes.
const accion = boton.dataset.tipo;
if (accion === 'editar') {
ejecutarEdicion();
} else if (accion === 'eliminar') {
ejecutarEliminacion();
}
});
function ejecutarEdicion() { console.log("Abriendo formulario de edición..."); }
function ejecutarEliminacion() { console.log("Borrando usuario..."); }
Lenguaje del código: JavaScript (javascript)
2.6.2. Técnicas para identificar «el elemento deseado»
Para saber si un elemento es el que buscas, puedes usar varios criterios dentro de tu if:
- Por Clase:
if (target.classList.contains('mi-clase'))- Uso: Muy común para estilos y comportamiento general.
- Por Atributo Data (
data-*):if (target.dataset.id)- Uso: El favorito de los profesionales. Permite separar la lógica del diseño (las clases).
- Por ID:
if (target.id === 'boton-especifico')- Uso: Solo si el elemento es único en toda la página.
- Por Tipo de Etiqueta:
if (target.tagName === 'BUTTON')- Uso: Cuando quieres capturar todos los elementos de un tipo, sin importar su clase.
2.6.3. ¿Por qué es importante para el rendimiento?
Cuando filtras correctamente al inicio de la función (usando el if (!boton) return;), el motor de JavaScript de Chrome o Firefox descarta el resto del código en milisegundos.
Si tienes una página muy compleja y no filtras, podrías estar haciendo cálculos pesados o búsquedas en el DOM innecesariamente cada vez que alguien hace un clic accidental, lo que causaría que la página se sienta «lenta» o «trabada».
2.7. Probar la delegación con elementos dinámicos creados posteriormente.
¿Qué significa «Elementos dinámicos»?
Son elementos que no existen en el HTML original cuando la página se carga por primera vez. Son creados por JavaScript mediante document.createElement() o .innerHTML como respuesta a:
- Una petición a una API (ej. cargar una lista de productos).
- Una acción del usuario (ej. agregar una nueva tarea a una lista).
- Un temporizador o cualquier otro evento asíncrono.
2.7.1. Ejemplo de elemento dinámicos (Sin Delegación)
Si intentas ponerle un evento a algo que no existe, JavaScript simplemente no lo encontrará.
JavaScript
// Digamos que el HTML está vacío al principio: <ul id="miLista"></ul>
// ESTO FALLARÁ:
const botones = document.querySelectorAll('.btn-eliminar');
// botones es una lista vacía [], porque aún no hay nada con esa clase.
botones.forEach(btn => {
btn.addEventListener('click', () => console.log("Borrando..."));
});
// Si 2 segundos después agregas un <li> con la clase .btn-eliminar,
// ¡NO TENDRÁ EL EVENTO! El código de arriba ya se ejecutó y terminó.Lenguaje del código: JavaScript (javascript)
2.7.2. La solución: Ejemplo con Delegación
Como el addEventListener está en el padre estático (el que siempre estuvo ahí), no le importa cuándo nació el hijo. El padre es como un detector de metales en un aeropuerto: no importa si el pasajero llegó temprano o tarde, en cuanto pase por el arco (el evento haga bubbling), será detectado.
JavaScript
// 1. Seleccionamos el padre que SI existe desde el inicio
const listaContenedor = document.querySelector('#lista-dinamica');
const btnAgregar = document.querySelector('#btn-agregar');
// 2. Aplicamos DELEGACIÓN en el padre
listaContenedor.addEventListener('click', (e) => {
// Usamos el filtrado que aprendimos antes
const botonEliminar = e.target.closest('.borrar-tarea');
if (botonEliminar) {
// Encontramos el <li> padre del botón para eliminarlo
const tarea = botonEliminar.parentElement;
tarea.remove();
console.log("Tarea eliminada correctamente");
}
});
// 3. Simulación de creación de elementos dinámicos
btnAgregar.addEventListener('click', () => {
const nuevaTarea = document.createElement('li');
nuevaTarea.innerHTML = `
Nueva Tarea ${Date.now()}
<button class="borrar-tarea">❌</button>
`;
listaContenedor.appendChild(nuevaTarea);
// ¡MAGIA!: No necesitamos agregarle un listener al nuevo botón.
// El 'listaContenedor' ya lo está escuchando.
});
Lenguaje del código: JavaScript (javascript)
2.7.3. ¿Por qué es esto mejor para tu proyecto?
MENOS BUGS: Evitas el error común de olvidar re-asignar eventos cuando actualizas el DOM.
CÓDIGO MÁS CORTO: No tienes que envolver cada creación de elemento en una función de «asignar evento».
MENOS USO DE MEMORIA: No importa si el usuario crea 100 o 100,000 tareas; solo hay un addEventListener activo en el navegador.
2.8. Mantener el listener simple, claro y fácil de mantener.
¿Qué significa “mantener el listener simple, claro y fácil de mantener”?
Significa que el addEventListener():
- No debe contener lógica compleja
- No debe crecer sin control
- Debe actuar como un dispatcher (despachador)
- La lógica real debe vivir en funciones separadas
👉 El listener decide qué hacer, no cómo hacerlo todo.
2.8.1. Ejemplo incorrecto: listener sobrecargado
❌ Código
lista.addEventListener('click', (event) => {
if (event.target.matches('.editar')) {
// 30 líneas de lógica
} else if (event.target.matches('.eliminar')) {
// 25 líneas de lógica
} else if (event.target.matches('.duplicar')) {
// 20 líneas de lógica
} else if (event.target.matches('.archivar')) {
// 15 líneas de lógica
}
});Lenguaje del código: PHP (php)
❌ Problemas
- Difícil de leer
- Difícil de testear
- Difícil de extender
- Riesgo de errores
- Listener “gigante”
📌 Mal diseño, aunque funcione.
2.8.2. Ejemplo correcto: listener como dispatcher
✅ Código
lista.addEventListener('click', manejarClickLista);
function manejarClickLista(event) {
const boton = event.target.closest('[data-action]');
if (!boton) return;
const accion = boton.dataset.action;
acciones[accion]?.(boton);
}
const acciones = {
editar: editarItem,
eliminar: eliminarItem,
duplicar: duplicarItem
};Lenguaje del código: JavaScript (javascript)
Explicación:
¿Qué hace el listener?
- Detecta el evento
- Filtra el objetivo
- Obtiene la acción
- Delegá la ejecución
📌 Nada más.
2.8.3. HTML limpio y semántico
<ul id="lista">
<li>
<button data-action="editar">Editar</button>
<button data-action="eliminar">Eliminar</button>
</li>
</ul>Lenguaje del código: HTML, XML (xml)
📌 El HTML define qué acción, no cómo.
2.8.4. Lógica separada en funciones
function editarItem(boton) {
console.log('Editar', boton.closest('li'));
}
function eliminarItem(boton) {
boton.closest('li').remove();
}
function duplicarItem(boton) {
const li = boton.closest('li');
lista.appendChild(li.cloneNode(true));
}Lenguaje del código: JavaScript (javascript)
Cada función:
- Tiene una sola responsabilidad
- Es testeable
- Es reutilizable
2.8.5. Beneficios reales de este enfoque
✔ Listener corto y legible
✔ Fácil agregar nuevas acciones
✔ Menos errores
✔ Código escalable
✔ Cumple SRP (Single Responsibility Principle)
2.8.6. Error común: lógica inline “rápida”
lista.addEventListener('click', (e) => {
if (e.target.matches('.eliminar')) {
e.target.closest('li').remove();
actualizarContador();
guardarEnLocalStorage();
mostrarToast();
}
});Lenguaje del código: PHP (php)
📌 Funciona hoy, problema mañana.
2.8.7. Resumen:
- Listener = controlador
- Funciones = lógica
- Menos código dentro del listener
- Más claridad y mantenibilidad
- Código profesional
3. Ejemplo delegación de eventos con imagen.

Aquí tienes la explicación paso a paso de lo que estás viendo:
1. El contenedor padre estable (El marco grande azul)
En la imagen, es el cuadro más grande que engloba a todo lo demás.
- En la realidad:
- Es un elemento de tu HTML que nunca desaparece, como un
div, unulo incluso elbody.
- Es un elemento de tu HTML que nunca desaparece, como un
- Por qué es «Estable»:
- Porque aunque los hijos de adentro cambien, el padre siempre está ahí para recibir los mensajes.
2. El Listener único (El icono de la antena/oído)
Observa que solo hay una antena en la parte superior donde dice addEventListener AQUÍ.
- La clave del rendimiento:
- En lugar de poner 10 antenas pequeñas (una en cada hijo), pusimos una sola antena potente en el jefe (el padre).
- Ahorro de memoria:
- Esto es lo que hace que tu aplicación sea rápida. JavaScript solo tiene que vigilar un punto en lugar de cientos.
3. El clic: Origen / Target (El cuadro naranja)
Ese cuadro naranja que brilla es el event.target.
- La acción:
- El usuario hizo clic exactamente ahí.
- Dinamismo:
- Ese cuadro podría haber sido creado hace un segundo por una API; no importa, el sistema lo detecta porque está dentro del territorio del padre.
4. El flujo de burbujeo / bubbling (La onda azul neón)
Esta es la parte más importante del dibujo. Mira cómo la energía sube desde el cuadro naranja hasta la antena.
- Qué es:
- Por naturaleza, en el DOM, los eventos no se quedan donde nacen. Suben hacia arriba, capa por capa.
- El fenómeno:
- El clic ocurre en el hijo, pero el evento «burbujea» hasta llegar al padre. Cuando la onda azul toca la antena (el listener), JavaScript ejecuta tu función.
¿Cómo lo procesa JavaScript? (El flujo lógico)
Cuando la antena (el padre) recibe esa señal azul, tu código hace lo siguiente internamente:
- Recibe el paquete:
- «¡Oye! Alguien hizo clic aquí adentro».
- Mira la etiqueta de origen:
- Revisa el
event.target(el cuadro naranja).
- Revisa el
- Filtra:
- «¿Eres un elemento que me interesa? (por ejemplo, ¿tienes la clase
.btn?)».
- «¿Eres un elemento que me interesa? (por ejemplo, ¿tienes la clase
- Actúa:
- «¡Sí lo eres! Entonces ejecuto la función de borrar/editar».
En resumen, la imagen te muestra que:
No necesitas perseguir a cada hijo pequeño. Solo necesitas un receptor en el lugar correcto (el padre) y dejar que la física de JavaScript (el Bubbling) traiga la información hacia ti.
