¿Qué es el burbujeo y la captura de eventos en JavaScript?
El Burbujeo y la Captura de Eventos representan el modelo de propagación de eventos en el DOM. Semánticamente, este flujo define el orden en que los listeners de los elementos ancestros reaccionan al evento que ocurre en un elemento hijo. La Captura viaja hacia abajo para interceptar, y el Burbujeo sube para reaccionar. Dominar estas fases es fundamental para una gestión de eventos eficiente y optimizada, como veremos en los ejemplos prácticos.

Índice
1 ¿Qué es el burbujeo y la captura de eventos en JavaScript?
Para responder esta pregunta, primero debemos explicar un concepto fundamental: el FLUJO DE EVENTOS.
Imagina el DOM de tu página web (los elementos HTML anidados: <html>, <body>, <div>, <button>) como una serie de cajas colocadas una dentro de la otra.
Cuando haces clic en el elemento más interno (por ejemplo, un botón), ese evento sobre el DOM (clic en un botón) no solo se queda en el botón; emprende un viaje por toda la jerarquía de las cajas (elementos) del DOM.
¿QUÉ ES UN EVENTO EN EL DOM?
Un evento en el DOM es una notificación que el navegador envía cuando algo ocurre dentro del documento HTML, ya sea una acción del usuario o un cambio interno del sistema.
Dicho de otra forma: un evento es una señal que indica que algo pasó, como un clic, una pulsación de tecla, el envío de un formulario, o incluso la carga completa de la página.
Este viaje del evento se llama FLUJO DE EVENTOS y consta de tres etapas principales:
1.1. Fase de captura (Capturing)
- ¿Qué es?
-
El evento comienza en la cima del árbol DOM (
windowodocument) y viaja hacia abajo de padre a hijo hasta encontrar el elemento donde se originó el clic, conocido como elemento objetivo (target). - Propósito
- Permite a los elementos ancestros interceptar el evento antes de que llegue a su destino final, dando control a contenedores externos sobre lo que sucede en los elementos internos.
- Comportamiento por defecto
-
Los listeners no se ejecutan en esta fase por defecto. Para que se activen durante esta etapa, es necesario indicarlo explícitamente usando
addEventListenercon el parámetro de captura.
1.2. Fase de destino (Target)
- ¿Qué es?
-
Es la fase en la que el evento llega exactamente al elemento que fue clickeado, es decir, el elemento objetivo (
target). - Propósito
- En este momento, se ejecutan los listeners adjuntos directamente a ese elemento, permitiendo manejar la acción específica que el usuario realizó.
1.3. Fase de burbujeo (Bubbling)
- ¿Qué es?
-
Una vez que el evento alcanza su objetivo, comienza a propagarse hacia arriba en el árbol DOM, desde el elemento hijo hasta sus elementos padres, llegando finalmente a
documentowindow. - Comportamiento por defecto
-
Este es el comportamiento predeterminado en JavaScript. Si agregas un listener con
addEventListenersin especificar el tercer parámetro, se ejecutará durante la fase de burbujeo. - Propósito
- Permite que los elementos ancestros reaccionen a eventos ocurridos en sus descendientes, siendo la base de la Delegación de Eventos, una técnica clave para manejar eventos de forma eficiente.
RESUMEN:
- Captura: El evento viaja hacia abajo al elemento. Es la fase de intercepción.
- Burbujeo: El evento viaja hacia arriba desde el elemento. Es la fase de reacción.

¿Qué es un Listener de Eventos?
Un Listener de Eventos (o manejador/observador de eventos) es una función de JavaScript que está esperando a que ocurra un evento específico (como un clic del ratón, una pulsación de tecla, o la carga de la página) en un elemento particular del DOM.
Su función es simple:
Es una función callback que se ejecuta solo y únicamente cuando el evento que está observando se dispara en el elemento al que fue adjuntado.
En la práctica, lo adjuntas usando el método addEventListener().
1.4. ¿Por qué existe este sistema?
✔ Para permitir delegación de eventos (muy útil en proyectos grandes)
✔ Para que los eventos se puedan manejar en diferentes niveles del DOM
✔ Para evitar poner demasiados listeners en elementos pequeños
✔ Para permitir interceptar, modificar o cancelar el evento en el camino.
2. Fase de captura (Capturing): ¿Cómo funciona?
Recuerda nuestra metáfora de las «cajas anidadas». La Fase de Captura es la primera parte del viaje del evento.
- Definición:
- La Captura ocurre cuando el evento viaja desde el elemento más externo (
window,document,<body>) hacia el elemento objetivo (el que fue clickeado).
- La Captura ocurre cuando el evento viaja desde el elemento más externo (
- Propósito:
- La Captura permite a un elemento padre (ancestral) reaccionar al evento antes de que este llegue al elemento hijo. Es como si el padre pudiera decir: «¡Un momento, déjame revisar este evento antes de que mi hijo lo procese!».
1.1. ¿Cómo se activa?
Por defecto, la mayoría de los listeners que adjuntas esperan a la fase de Burbujeo (la subida). Para que un listener se active en la fase de Captura (la bajada), debes indicarlo explícitamente usando el tercer argumento del método addEventListener().
La sintaxis es la siguiente:
elemento.addEventListener(tipoDeEvento, manejador, opciones);Lenguaje del código: CSS (css)
Donde opciones puede ser un simple valor booleano (true) o un objeto { capture: true }.
| Valor del tercer argumento | Fase en la que se activa el listener |
false (o se omite) | Burbujeo (por defecto) |
true o { capture: true } | Captura |
1.2. Ejemplo práctico: Captura vs. Burbujeo
Vamos a crear una estructura simple de tres elementos anidados para ver el orden exacto de ejecución.
Estructura HTML
<div id="contenedor" style="background: lightblue; padding: 20px;">
CONTENEDOR
<div id="caja" style="background: lightcoral; padding: 20px;">
CAJA
<button id="boton">CLICK AQUÍ</button>
</div>
</div>
Lenguaje del código: HTML, XML (xml)
Código JavaScript
Vamos a adjuntar un listener a cada elemento, pero uno de ellos (el caja) lo pondremos en modo Captura.
// Obtener referencias a los elementos
const contenedor = document.getElementById('contenedor');
const caja = document.getElementById('caja');
const boton = document.getElementById('boton');
// 1. Contenedor (Padre): Modo Burbujeo (por defecto)
contenedor.addEventListener('click', function() {
console.log('1. Burbujeo: Contenedor (Padre)');
});
// 2. Caja (Hijo): Modo CAPTURA (tercer argumento: true)
caja.addEventListener('click', function() {
console.log('2. CAPTURA: Caja (Hijo)');
}, true); // <-- ¡Aquí está la clave!
// 3. Botón (Elemento Destino): Modo Burbujeo (por defecto)
boton.addEventListener('click', function() {
console.log('3. Destino: Botón (Target)');
});
// 4. Contenedor (Padre): Modo CAPTURA
contenedor.addEventListener('click', function() {
console.log('4. CAPTURA: Contenedor (Padre)');
}, { capture: true }); // <-- También en modo Captura
Lenguaje del código: JavaScript (javascript)
➡️ Resultado al hacer clic en el BOTÓN
Al hacer clic en el Botón, la consola mostrará la siguiente secuencia de ejecución:
| Fase | Elemento | Mensaje en Consola | Explicación |
| Captura (Bajando) | Contenedor | 4. CAPTURA: Contenedor (Padre) | Se activa primero, pues es el primero en la ruta de bajada con capture: true. |
| Captura (Bajando) | Caja | 2. CAPTURA: Caja (Hijo) | Se activa segundo, pues sigue bajando con capture: true. |
| Destino | Botón | 3. Destino: Botón (Target) | Se ejecuta el listener del elemento clickeado. |
| Burbujeo (Subiendo) | Contenedor | 1. Burbujeo: Contenedor (Padre) | El evento sube y activa el listener de Burbujeo del Contenedor. |
CONCLUSIÓN: Los listeners en modo Captura siempre se ejecutan antes que los listeners en modo Burbujeo, incluso si están en el mismo elemento (el contenedor).
2. Fase de burbujeo (Bubbling): ¿Cómo funciona?
La Fase de Burbujeo (Bubbling) es, sin duda, la parte más importante y la que usarás el 90% del tiempo en JavaScript, ya que es el comportamiento predeterminado del DOM.
Es la segunda mitad del FLUJO DE EVENTOS:
- ¿Qué es?
- Ocurre inmediatamente después de que el evento ha llegado y se ha procesado en el elemento objetivo (Target). El evento comienza a viajar hacia arriba por la jerarquía del DOM, desde el elemento que fue clickeado hasta la raíz (
documentowindow).
- Ocurre inmediatamente después de que el evento ha llegado y se ha procesado en el elemento objetivo (Target). El evento comienza a viajar hacia arriba por la jerarquía del DOM, desde el elemento que fue clickeado hasta la raíz (
- Comportamiento por defecto:
- Todos los event listeners que adjuntas usando
addEventListener('click', handler)(sin el tercer argumento) están configurados para activarse durante esta fase.
- Todos los event listeners que adjuntas usando
- Propósito:
- Permite que los elementos padres (los contenedores) puedan reaccionar al evento que ocurrió en uno de sus hijos. Esto nos permite implementar la técnica fundamental de la Delegación de Eventos (que veremos más adelante).
2.1.. Ejemplo práctico: El orden de ejecución:
Usaremos la misma estructura anidada que antes para demostrar cómo sube el evento.
Estructura HTML
<div id="padre" style="background: lightblue; padding: 20px;">
PADRE
<div id="hijo" style="background: lightcoral; padding: 20px;">
HIJO
<button id="boton">CLICK AQUÍ</button>
</div>
</div>Lenguaje del código: HTML, XML (xml)
Código JavaScript (Solo Burbujeo)
Adjuntamos listeners de evento a los tres elementos. Como no especificamos el tercer argumento, todos usarán el modo por defecto: Burbujeo.
// Obtener referencias
const padre = document.getElementById('padre');
const hijo = document.getElementById('hijo');
const boton = document.getElementById('boton');
// Todos los listeners se activarán durante la fase de Burbujeo (Subida)
// 1. Botón (Elemento Destino)
boton.addEventListener('click', function() {
console.log('1. Click en: BOTÓN (Target)');
});
// 2. Hijo (Contenedor intermedio)
hijo.addEventListener('click', function() {
console.log('2. Evento Burbujea hacia: HIJO');
});
// 3. Padre (Contenedor externo)
padre.addEventListener('click', function() {
console.log('3. Evento Burbujea hacia: PADRE');
});Lenguaje del código: JavaScript (javascript)
➡️ Resultado al hacer clic en el BOTÓN:
Cuando haces clic en el Botón, el evento se origina allí y luego «burbujea» hacia arriba:
- El listener del BOTÓN se dispara.
- El evento sube al elemento HIJO, y su listener se dispara.
- El evento sube al elemento PADRE, y su listener se dispara.
Mensajes en Consola:
1. Click en: BOTÓN (Target)
2. Evento Burbujea hacia: HIJO
3. Evento Burbujea hacia: PADRE
3. Detener la propagación: stopPropagation() y stopImmediatePropagation()
Detener la propagación significa cortar el recorrido del evento en el DOM.
El evento deja de viajar:
- no sigue bajando (captura)
- no sigue subiendo (burbujeo)
El evento sigue existiendo, pero no llega a otros listeners.
NOTA! — Riesgo de abuso: Si detienes la propagación, cualquier listener que esté más arriba en el DOM (en los elementos ancestros) y que espere reaccionar a ese evento, simplemente no se ejecutará. Esto puede hacer que tu código sea muy difícil de depurar (debuggear) o que rompa la funcionalidad de componentes de terceros.
🔹 Métodos para detener propagación:
JavaScript tiene dos niveles de bloqueo:
event.stopPropagation()event.stopImmediatePropagation()
3.1.event.stopPropagation()
🔹 Qué hace
Detiene la propagación hacia otros elementos, pero no detiene otros listeners del mismo elemento.
🔹 ¿Cuándo Usarla?
Se usa cuando sabes con certeza que el evento ha sido manejado de manera completa por el elemento actual y que cualquier reacción de los elementos padres sería incorrecta o redundante.
🔹 Ejemplo clásico:
Tienes un menú desplegable dentro de un área de contenido. Si el usuario hace clic dentro del menú desplegable, el evento burbujeará hacia el área de contenido, que podría tener un listener adjunto para «cerrar el menú desplegable si se hace clic fuera». En este caso, debes detener la propagación dentro del menú para evitar que el listener del contenedor externo lo cierre inmediatamente.
3.1.1. Ejemplo de código con stopPropagation():
Usaremos la estructura anidada y demostraremos cómo el evento deja de subir al padre.
HTML
<div id="padre" style="background: lightblue; padding: 20px;">
PADRE
<button id="boton">CLICK AQUÍ</button>
</div>
Lenguaje del código: HTML, XML (xml)
JavaScript
const padre = document.getElementById('padre');
const boton = document.getElementById('boton');
// Listener en el Padre
padre.addEventListener('click', function() {
console.log('1. Listener del PADRE ejecutado. (Nunca se verá)');
});
// Listener en el Botón (donde se originará el evento)
boton.addEventListener('click', function(event) {
console.log('2. Listener del BOTÓN ejecutado.');
// Detenemos el burbujeo aquí mismo
event.stopPropagation();
console.log('3. Propagación detenida. El PADRE no será notificado.');
});
Lenguaje del código: JavaScript (javascript)
Resultado al hacer clic en el BOTÓN:
2. Listener del BOTÓN ejecutado.
3. Propagación detenida. El PADRE no será notificado.
Explicación: El listener del padre nunca se ejecuta porque stopPropagation() cortó el viaje del evento hacia arriba (Burbujeo) justo después de que el botón lo procesó.
3.2. event.stopImmediatePropagation()
Esta función es aún más drástica y tiene un impacto adicional.
🔹 ¿Cuál es la diferencia con stopPropagation()?
stopImmediatePropagation() hace dos cosas:
- Detiene el viaje del evento (como
stopPropagation()). - Detiene la ejecución de cualquier otro listener adjunto al mismo elemento (el elemento actual).
Es el freno más fuerte.
3.2.1. Ejemplo de código con stopImmediatePropagation()
Adjuntamos dos listeners al mismo botón:
JavaScript
const boton = document.getElementById('boton');
// Listener 1 (Se ejecuta primero)
boton.addEventListener('click', function(event) {
console.log('1. Listener A: Ejecutado.');
// Detenemos inmediatamente la propagación Y la ejecución de otros listeners en el BOTÓN
event.stopImmediatePropagation();
console.log('2. Listener A: stopImmediatePropagation() llamado.');
});
// Listener 2 (Nunca se ejecutará)
boton.addEventListener('click', function(event) {
console.log('3. Listener B: Ejecutado. (¡Nunca se verá!)');
});
// El Listener del Padre tampoco se ejecutará
padre.addEventListener('click', function() {
console.log('4. Listener del PADRE: Ejecutado. (¡Nunca se verá!)');
});
Lenguaje del código: JavaScript (javascript)
Resultado al hacer clic en el BOTÓN:
1. Listener A: Ejecutado.
2. Listener A: stopImmediatePropagation() llamado.
Explicación: El Listener B (en el mismo botón) y el Listener del Padre (arriba) fueron ignorados por completo. Esta es la función más poderosa y debe usarse con extrema precaución, típicamente en casos donde un listener anterior debe anular completamente cualquier procesamiento posterior.
4. event.preventDefault(): Anular el comportamiento nativo
4.1. ¿Qué es event.preventDefault()?
event.preventDefault() es un método del objeto Event que detiene la acción que el navegador realiza por defecto cuando ocurre un evento.
- Propósito: Detiene la acción nativa y automática del navegador que está asociada con el evento, sin interferir en el viaje del evento (la propagación, o sea, la captura y el burbujeo).
4.2. Casos de uso comunes
Hay dos escenarios donde su uso es casi obligatorio:
| Escenario | Comportamiento Nativo | Lo que preventDefault() detiene |
|---|---|---|
Clic en un Enlace (<a>) |
Navega a la URL especificada en el atributo href. |
Impide la navegación, permitiéndote manejar el clic con JavaScript (ej. Single Page Applications). |
Envío de un Formulario (<form>) |
Envía los datos del formulario al servidor y, crucialmente, recarga la página (o navega a la página de destino). | Impide la recarga, permitiéndote enviar los datos de forma asíncrona (AJAX / Fetch). |
4.3. Ejemplo práctico con formulario:
Este es el caso de uso más frecuente. Queremos validar y enviar el formulario usando JavaScript sin que la página se recargue.
Estructura HTML
<form id="registroForm">
<input type="text" placeholder="Nombre de usuario">
<button type="submit">Registrar</button>
</form>Lenguaje del código: HTML, XML (xml)
Código JavaScript
const formulario = document.getElementById('registroForm');
formulario.addEventListener('submit', function(event) {
// 1. Validar datos (aquí iría tu lógica de validación)
const datosValidos = true; // Simulación de validación exitosa
if (datosValidos) {
// 2. ¡CRUCIAL! Detenemos el envío HTTP nativo y la recarga de la página.
event.preventDefault();
console.log('Comportamiento nativo de recarga PREVENIDO.');
// 3. Aquí iría la lógica para enviar los datos vía Fetch/AJAX:
console.log('Enviando datos de forma asíncrona con JavaScript...');
} else {
// Si no es válido, no llamamos a preventDefault() y podríamos permitir la acción por defecto
// o mostrar un mensaje de error y detener la ejecución del handler (return).
console.log('Validación fallida. No se envía el formulario.');
}
});Lenguaje del código: JavaScript (javascript)
Explicación: Si no incluyeras event.preventDefault(), al hacer clic en el botón, verías la consola por una fracción de segundo antes de que la página se recargue completamente. Al llamarlo, el navegador se abstiene de su acción predeterminada y solo ejecuta tu código JavaScript.
4.4. Relación con la propagación (Burbujeo y Captura)
Es vital entender que event.preventDefault() y event.stopPropagation() hacen cosas totalmente diferentes:
| Propiedad / Método | Función | ¿Afecta al Viaje del Evento? |
|---|---|---|
event.stopPropagation() |
Detiene el viaje del evento (Captura / Burbujeo). | SÍ. Los padres y abuelos no se enteran. |
event.preventDefault() |
Detiene la acción nativa del navegador. | NO. El evento sigue burbujeando o capturando. |
EJEMPLO DE LA DIFERENCIA:
Imagina que tienes un enlace (<a>) dentro de un div.
JavaScript
divContenedor.addEventListener('click', function(e) {
console.log('El DIV fue notificado del click.');
// El enlace sigue su flujo normal (burbujea), aunque el navegador no navegue.
});
enlace.addEventListener('click', function(e) {
e.preventDefault(); // Detiene la navegación (acción nativa)
console.log('El ENLACE fue clickeado, pero la navegación fue prevenida.');
// e.stopPropagation(); // No se usa aquí, el evento seguirá subiendo al divContenedor
});
Lenguaje del código: JavaScript (javascript)
En este caso, si haces clic en el enlace, verás:
- «El ENLACE fue clickeado, pero la navegación fue prevenida.»
- «El DIV fue notificado del click.» (El burbujeo continuó sin problemas).
Si hubieras añadido e.stopPropagation() al enlace, el divContenedor no se habría notificado.
4.5. Errores comunes:
❌ Creer que preventDefault() detiene burbujeo
❌ Usarlo sin que exista una acción por defecto
❌ Olvidarlo en formularios
❌ Usarlo con passive: true
❌ Bloquear UX sin razón (scroll, teclado)
4.6. Consejos en su uso:
🔸 Usá preventDefault() solo cuando exista una acción automática
🔸 No lo mezcles con propagación sin entender efectos
🔸 Formularios modernos → siempre con preventDefault()
5. Delegación de eventos en la fase burbujeo.
La delegación de eventos es un patrón de diseño que aprovecha la Fase de Burbujeo para optimizar el rendimiento y el manejo de elementos dinámicos en el DOM.
5.1. ¿Qué es la delegación de eventos?
En lugar de adjuntar un event listener a cada elemento hijo (por ejemplo, 100 botones o ítems de una lista), adjuntamos un único listener al elemento Padre (el contenedor).
- Principio
-
Cuando un elemento hijo es clickeado, el evento burbujea hacia arriba en el DOM. El elemento padre intercepta el evento y utiliza las propiedades del objeto
Eventpara determinar qué acción ejecutar según el elemento objetivo. - Ventaja Clave
- Es la forma más eficiente de manejar eventos en listas dinámicas, ya que un solo listener en el padre cubre automáticamente a todos los hijos, incluso a aquellos que se agregan al DOM después de la carga inicial.
- Principio: Cuando un elemento hijo es clickeado, el evento burbujea hacia arriba. El elemento Padre intercepta el evento que le llega y luego usa las propiedades del objeto
Eventpara determinar qué acción realizar. - Ventaja Clave: Es la forma más eficiente de manejar eventos en listas dinámicas (elementos que se añaden al DOM después de la carga inicial), ya que el listener del padre cubre automáticamente a todos los hijos futuros.
5.2. Los pilares de la delegación: target y currentTarget
El manejo del evento delegado se logra analizando las dos propiedades del objeto Event:
| Propiedad | Definición | Rol en la Delegación |
|---|---|---|
event.target |
El elemento más profundo del DOM donde se originó el evento (donde se hizo clic). | La clave. Usamos el target para identificar qué elemento hijo fue clickeado y aplicar la lógica correcta. |
event.currentTarget |
El elemento del DOM al que se ha adjuntado el listener actualmente en ejecución. | El delegado. Siempre será el elemento padre (el contenedor) que está escuchando. |
5.3. ¿Cómo se Implementa?
La implementación se basa en tres pasos: adjuntar el listener al padre, permitir el burbujeo y usar event.target para filtrar la acción.
🧩EJEMPLO práctico: Lista dinámica
Imagina una lista de tareas (todo list) donde los ítems se añaden o eliminan constantemente. Sin delegación, tendrías que adjuntar un listener a cada nuevo ítem. Con delegación, solo necesitas uno en la lista completa.
Estructura HTML
<ul id="listaTareas">
<li class="tarea">Tarea 1: Comprar leche <button class="eliminar">X</button></li>
<li class="tarea">Tarea 2: Estudiar JS <button class="eliminar">X</button></li>
</ul>
<button id="agregarBtn">Agregar Tarea</button>
Lenguaje del código: HTML, XML (xml)
Código JavaScript (Implementación de delegación)
const lista = document.getElementById('listaTareas');
const agregarBtn = document.getElementById('agregarBtn');
// 1. **ÚNICO LISTENER** adjunto al elemento PADRE (<ul>)
lista.addEventListener('click', function(event) {
// El 'currentTarget' es 'lista', pero el 'target' es el <li> o el <button> clickeado.
// Verificamos si el elemento clickeado (target) tiene la clase 'eliminar'
if (event.target.classList.contains('eliminar')) {
// En este punto, 'event.target' es el botón 'X'.
// event.target.parentElement nos da el <li> padre que queremos eliminar
const itemAEliminar = event.target.parentElement;
lista.removeChild(itemAEliminar);
console.log(`Se eliminó la tarea: ${itemAEliminar.textContent.trim()}`);
}
// También podemos manejar clics en el <li> en sí
if (event.target.classList.contains('tarea')) {
// Marcamos la tarea como completada/incompleta
event.target.classList.toggle('completada');
console.log(`Estado de tarea cambiado: ${event.target.textContent.trim()}`);
}
});
// 2. Función para añadir elementos dinámicamente
agregarBtn.addEventListener('click', () => {
const nuevoLi = document.createElement('li');
nuevoLi.className = 'tarea';
nuevoLi.innerHTML = `Tarea ${lista.children.length + 1} (Nueva) <button class="eliminar">X</button>`;
lista.appendChild(nuevoLi);
console.log('Nueva tarea añadida.');
// NOTA: El nuevo <li> funciona inmediatamente sin necesidad de un nuevo listener!
});
Lenguaje del código: JavaScript (javascript)
Explicación:
- Añadir Elemento Dinámico: Cuando haces clic en
Agregar Tarea, se añade un nuevo<li>al DOM. - El Clic: Haces clic en el botón
Xdel nuevo<li>. - El Burbujeo: El evento se origina en el botón
Xy burbujea hacia arriba, llegando al<ul>(nuestrolista). - El Listener se Activa: El único listener en el
<ul>se dispara. - Verificación: Dentro del listener, comprobamos:
if (event.target.classList.contains('eliminar')). ¡Elevent.targetes el botónXque fue clickeado, sin importar cuándo fue añadido! - Acción: Se ejecuta la lógica de eliminación.
Conclusión: Un solo listener maneja tanto los elementos estáticos como los generados dinámicamente, haciendo tu código ligero y robusto.
🧩ESCENARIO REAL
LISTA DE TAREAS CON DELEGACIÓN DE EVENTOS
Haz clic en el texto de la tarea para marcarla como completada. Usa el botón «X» para eliminarla.
Añade nuevas tareas y verás que el botón «X» funciona automáticamente gracias al Listener único en el contenedor padre.
- Tarea Estática 1: Revisar Correo
- Tarea Estática 2: Preparar Café
6. Orden de ejecución: Captura vs. Burbujeo.
Cuando un evento ocurre en el DOM, JavaScript sigue SIEMPRE este orden fijo:
1️⃣ Captura (↓ desde window hasta el target)
2️⃣ Target (elemento donde ocurrió el evento)
3️⃣ Burbujeo (↑ desde el target hasta window)Lenguaje del código: JavaScript (javascript)
👉 La prioridad NO depende del orden en que escribís el código, sino de la fase en la que está registrado el listener.
6.1. Regla fundamental de prioridad
La prioridad no depende de dónde colocaste el listener en el código, sino de la fase que está escuchando:
- Captura Siempre Primero
-
Los listeners configurados en modo Captura (
{ capture: true }) en un elemento padre se ejecutan antes que cualquier listener de Burbujeo, ya sea en el mismo nivel o en niveles inferiores del DOM. - Destino en el Medio
-
Cuando el evento alcanza el elemento que fue clickeado (
target), los listeners de ese elemento se ejecutan en el orden en que fueron registrados, sin importar si son de Captura o de Burbujeo. - Burbujeo Siempre Último
- Los listeners de Burbujeo en los elementos ancestros se ejecutan solo después de que el evento pasó por el elemento destino y comienza su recorrido ascendente por el DOM.
- Captura Siempre Primero: Cualquier listener que esté configurado en modo Captura (
{ capture: true }) en un elemento padre siempre se ejecutará antes que cualquier listener de Burbujeo en el mismo nivel o en los niveles inferiores. - Destino en el Medio: Cuando el evento llega al elemento que fue clickeado (el target), los listeners de ese elemento se ejecutan en el orden en que fueron adjuntados, sin importar si son de Captura o Burbujeo.
- Burbujeo Siempre Último: Los listeners de Burbujeo en los elementos ancestros se ejecutan solo después de que el evento ha pasado por el Destino y comienza a subir.
6.2. Ejemplo de orden de ejecución:
Vamos a usar una estructura simple y adjuntaremos listeners en ambas fases para demostrar el orden exacto.
Estructura HTML
<div id="padre" style="background: lightblue; padding: 20px;">
PADRE
<div id="hijo" style="background: lightcoral; padding: 20px;">
HIJO
</div>
</div>
Lenguaje del código: HTML, XML (xml)
Código JavaScript
Adjuntamos listeners al padre y al hijo, mezclando los modos Captura y Burbujeo. El evento se disparará haciendo clic en el hijo.
const padre = document.getElementById('padre');
const hijo = document.getElementById('hijo');
// 1. LISTENER DEL PADRE en modo CAPTURA (Se ejecutará primero en la Bajada)
padre.addEventListener('click', function() {
console.log('1. PADRE - Fase de CAPTURA');
}, { capture: true });
// 2. LISTENER DEL HIJO en modo BURBUJEO (El Destino. Se ejecuta después de la captura del padre)
hijo.addEventListener('click', function() {
// ESTE ES EL ELEMENTO DESTINO (TARGET)
console.log('2. HIJO - Fase de DESTINO (Burbujeo por defecto)');
});
// 3. OTRO LISTENER DEL HIJO en modo CAPTURA (Otro listener en el Destino)
hijo.addEventListener('click', function() {
// Si hay múltiples listeners en el DESTINO, se ejecutan en orden de adición
console.log('3. HIJO - Fase de DESTINO (Captura)');
}, { capture: true });
// 4. LISTENER DEL PADRE en modo BURBUJEO (Se ejecutará al final, en la Subida)
padre.addEventListener('click', function() {
console.log('4. PADRE - Fase de BURBUJEO');
});
Lenguaje del código: JavaScript (javascript)
➡️ Resultado al hacer clic en el HIJO
El orden de ejecución de la consola será:
- PADRE – Fase de CAPTURA(El evento está bajando, activa el listener de Captura del Padre.)
- HIJO – Fase de DESTINO (Burbujeo por defecto)(El evento llega al Destino. Se ejecutan los listeners del Hijo en el orden en que se definieron.)
- HIJO – Fase de DESTINO (Captura)(Continúa ejecutando el segundo listener del Destino.)
- PADRE – Fase de BURBUJEO(El evento ha procesado el Destino y empieza a subir, activando el listener de Burbujeo del Padre.)
RESUMEN DEL ORDEN:
- Captura de Ancestros
-
Todos los listeners configurados con
{ capture: true }se ejecutan desdedocumenthasta el padre inmediato del elemento destino, siguiendo el recorrido descendente del evento. - Listeners del Destino
-
En el elemento objetivo (
target), se ejecutan todos los listeners adjuntos, sin importar si fueron registrados en Captura o Burbujeo, y en el orden exacto en que fueron añadidos. - Burbujeo de Ancestros
-
Los listeners de Burbujeo (modo por defecto) se ejecutan desde el padre inmediato del destino hasta
document, acompañando el recorrido ascendente del evento por el DOM.
