• Sobre mí
  • Proyectos
  • Contacto
  • Blog

Apasionado por el desarrollo web y el diseño de interfaces intuitivas.

Cada error es un paso. Cada línea, un aprendizaje.

📍   Salou, Tarragona, España ✉️   [email protected] 📞   +34 641932611

Curriculum

Ver mi curriculum completo

Mapa del sitio

  • Inicio
  • Blog
  • Proyectos
  • Sobre mi
  • Mi misión
  • Sitemap

Contácto

  • Página de contácto
  • LinkedIn
  • GitHub

2026 Frankuxui - Todos los derechos reservados

  • Términos y condiciones
  • Política de privacidad
  • Política de cookies
Imagen de unsplash creada por Frans Ruiter
Imagen de unsplash creada por Frans Ruiter
  • JavaScript

Articulo publicado el 13 de julio de 2026

Por Frank Esteban Isdray Junco

Event Delegation en JavaScript Vanilla: Optimiza tu DOM

Cómo reducir listeners y mejorar el rendimiento usando bubbling de eventos

La gestión de eventos en DOM es una tarea cotidiana en el desarrollo frontend. Sin embargo, cuando trabajas con listas dinámicas, tablas grandes o estructuras complejas, agregar un listener a cada elemento puede convertirse rápidamente en un problema de rendimiento y mantenibilidad.

Event delegation es una técnica poderosa que resuelve exactamente este problema, aprovechando el bubbling natural de eventos en el DOM. En este post, exploraremos cómo funcionan, cuándo usarlos y cómo implementarlos en JavaScript vanilla.

¿Qué es event delegation?

Event delegation es un patrón de programación que aprovecha el mecanismo de bubbling de eventos del DOM. En lugar de adjuntar un listener a cada elemento individual, adjuntas un único listener a un contenedor padre que captura todos los eventos que burbujean desde sus descendientes.

El navegador ejecuta los listeners en esta fase:

  1. Capturing phase: el evento desciende desde window hasta el elemento target.
  2. Target phase: el evento alcanza el elemento que lo disparó.
  3. Bubbling phase: el evento asciende nuevamente hacia window.

Por defecto, addEventListener escucha en la fase de bubbling. Esto significa que si un usuario hace clic en un elemento hijo, el evento "burbujea" hacia arriba, permitiendo que un listener en el padre lo capture.

Ventajas principales

Eficiencia de memoria y rendimiento

  • Menos listeners en memoria: un listener en lugar de cientos o miles.
  • Mejor rendimiento: menos peso en la creación y destrucción de listeners.

Escalabilidad y mantenibilidad

  • Soporte dinámico: funciona automáticamente con elementos agregados después de que se define el listener.
  • Código más limpio: lógica centralizada en un único lugar.

Ejemplo básico: lista de tareas

Imagina una lista de tareas donde los usuarios pueden hacer clic para marcar elementos como completados.

Approach incorrecto (sin delegation)

javascript
const tasks = document.querySelectorAll(".task-item");

tasks.forEach((task) => {
  task.addEventListener("click", () => {
    task.classList.toggle("completed");
  });
});

Este enfoque funciona para elementos estáticos, pero si agregas nuevas tareas dinámicamente, el nuevo elemento no tendrá el listener asignado. Además, si tienes 10,000 tareas, crearás 10,000 listeners.

Approach correcto (con delegation)

javascript
const taskList = document.querySelector(".task-list");

taskList.addEventListener("click", (event) => {
  // Verifica que el clic ocurrió en un elemento con la clase 'task-item'
  if (event.target.closest(".task-item")) {
    const taskItem = event.target.closest(".task-item");
    taskItem.classList.toggle("completed");
  }
});

Ahora, sin importar cuántas tareas tengas o cuántas agregues dinámicamente, un único listener maneja todos los eventos.

Usando event.target y element.closest()

Para identificar qué elemento disparó el evento dentro del delegado, tienes dos herramientas principales:

event.target

Devuelve el elemento exacto que disparó el evento.

javascript
document.addEventListener("click", (event) => {
  console.log(event.target); // El elemento que fue clickeado
});

element.closest(selector)

Busca el elemento más cercano que coincida con el selector, ascendiendo en el árbol DOM. Si no encuentra coincidencia, devuelve null.

javascript
document.addEventListener("click", (event) => {
  const button = event.target.closest("button");
  if (button) {
    console.log("Se hizo clic en un botón:", button.textContent);
  }
});

Usar closest() es más robusto que solo event.target porque funciona incluso si el usuario hace clic en elementos hijo (como un <span> dentro de un <button>).

Casos de uso reales

1. Tabla dinámicas con acciones

javascript
const tableBody = document.querySelector("tbody");

tableBody.addEventListener("click", (event) => {
  if (event.target.closest(".delete-btn")) {
    const row = event.target.closest("tr");
    const rowId = row.dataset.id;
    deleteRow(rowId);
    row.remove();
  }

  if (event.target.closest(".edit-btn")) {
    const row = event.target.closest("tr");
    openEditDialog(row.dataset.id);
  }
});

2. Formularios con validación en tiempo real

javascript
const form = document.querySelector("form");

form.addEventListener("input", (event) => {
  if (event.target.type === "email") {
    const isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(event.target.value);
    event.target.classList.toggle("invalid", !isValid);
  }

  if (event.target.type === "number") {
    const isValid = event.target.value > 0;
    event.target.classList.toggle("invalid", !isValid);
  }
});

3. Navegación con submenús

javascript
const nav = document.querySelector("nav");

nav.addEventListener("click", (event) => {
  const menuItem = event.target.closest(".menu-item");
  if (!menuItem) return;

  const submenu = menuItem.querySelector(".submenu");
  if (submenu) {
    submenu.classList.toggle("visible");
    event.preventDefault();
  }
});

Eventos que NO burbujean

No todos los eventos burbujean. Algunos eventos importantes que no burbujean incluyen:

  • focus / blur
  • load / unload
  • scroll
  • resize
  • reset / submit (en algunos navegadores)
  • play / pause (audio/video)
  • mouseenter / mouseleave

Para eventos que no burbujean, necesitas usar la fase de capturing o adjuntar listeners directamente a los elementos.

javascript
// Para focus, debes usar el tercer parámetro (capturing phase)
document.addEventListener(
  "focus",
  (event) => {
    console.log("Elemento enfocado:", event.target);
  },
  true
); // true = capturing phase

Performance: Medición real

Veamos cómo event delegation mejora el rendimiento:

javascript
console.time("Sin delegation");
const items = document.querySelectorAll(".item");
items.forEach((item) => {
  item.addEventListener("click", () => {
    item.classList.add("active");
  });
});
console.timeEnd("Sin delegation");

console.time("Con delegation");
const container = document.querySelector(".container");
container.addEventListener("click", (event) => {
  if (event.target.closest(".item")) {
    event.target.closest(".item").classList.add("active");
  }
});
console.timeEnd("Con delegation");

Con 1,000 elementos:

  • Sin delegation: ~15-20ms para crear listeners.
  • Con delegation: ~1-2ms para un único listener.

En aplicaciones con listas dinámicas, la diferencia es aún más marcada porque con delegation, los nuevos elementos funcionan instantáneamente sin código adicional.

Consideraciones y limitaciones

¿Cuándo NO usar event delegation?

Limitaciones técnicas

Eventos que no burbujean
  • Eventos que no burbujean: Si necesitas capturar focus, blur o scroll, la delegation no es una opción.

Casos de menor impacto

  • Eventos muy específicos: Si solo tienes un par de elementos, la sobrecarga de delegation no se justifica.
  • Eventos con comportamiento asíncrono: Si necesitas lógica compleja antes de que el evento llegue al padre.

Evitar captura global

javascript
// ❌ No hagas esto: captura TODOS los clics del documento
document.addEventListener("click", (event) => {
  // Lógica excesivamente genérica
});

Es mejor adjuntar el listener al contenedor más específico:

javascript
// ✅ Mejor: limita el scope al contenedor relevante
const userList = document.querySelector(".user-list");
userList.addEventListener("click", (event) => {
  // Lógica enfocada
});

Conclusión

Event delegation es una técnica esencial para escribir JavaScript vanilla eficiente. Cuando la uses correctamente:

  • Reduces el número de listeners en memoria.
  • Tu código es más mantenible y escalable.
  • Ganas soporte automático para elementos dinámicos.
  • Mejoras significativamente el rendimiento en DOM grandes.

La clave está en elegir el contenedor correcto, usar event.target.closest() para identificar elementos, y recordar que no todos los eventos burbujean.

¿Utilizas event delegation en tus proyectos? Comparte tu experiencia en comentarios.

En este artículo

  1. ¿Qué es event delegation?
  2. Ventajas principales
  3. Eficiencia de memoria y rendimiento
  4. Escalabilidad y mantenibilidad
  5. Ejemplo básico: lista de tareas
  6. Approach incorrecto (sin delegation)
  7. Approach correcto (con delegation)
  8. Usando event.target y element.closest()
  9. event.target
  10. element.closest(selector)
  11. Casos de uso reales
  12. 1. Tabla dinámicas con acciones
  13. 2. Formularios con validación en tiempo real
  14. 3. Navegación con submenús
  15. Eventos que NO burbujean
  16. Performance: Medición real
  17. Consideraciones y limitaciones
  18. ¿Cuándo NO usar event delegation?
  19. Limitaciones técnicas
  20. Casos de menor impacto
  21. Evitar captura global
  22. Conclusión

Articulos relacionados

Estos articulos pueden interesarte si te gusto este articulo.

Imagen de unsplash creada por Peter Burdon
Imagen del articulo Fotografía de Unsplash
13 de julio de 2026
JavaScript

Debounce y Throttle: Controla la frecuencia de ejecución de funciones

Optimiza el rendimiento de tu aplicación limitando cuántas veces se ejecutan listeners de eventos y funciones costosas

Imagen de unsplash creada por Benjamin Chambon
Imagen del articulo Fotografía de Unsplash
13 de julio de 2026
JavaScript

Event Delegation: Optimiza tus listeners en JavaScript

Maneja eventos en múltiples elementos con un único listener y mejora el rendimiento de tu aplicación

Fotografía de unsplash creada por Carter Obasohan
Imagen del articulo Los atributos data-* permiten enlazar datos y comportamiento en HTML
8 de julio de 2026
HTML
·JavaScript

Atributos data-* en HTML: Almacena datos personalizados en tu DOM

Aprende a usar los atributos data-* para guardar información en elementos HTML y accederla desde JavaScript de forma segura y semántica