El modelo de eventos en JavaScript se basa en un sistema de propagación que permite a los eventos pasar de padres a hijos y viceversa a través de la estructura del documento.
Es importante entender el modelo para controlar el orden en el que responden a un evento los elementos del DOM.
Por ejemplo, cuando hacemos click en un botón de un formulario, estamos haciendo click tanto en el formulario, como en el botón.
¿Qué elemento quieres que reaccione porimero click? ¿Y cómo se lo indicamos a JavaScript? Aquí es donde entra el modelo de propagación de eventos.
Este modelo tiene tres fases:
Fase | Orden de Ejecución |
---|---|
Captura (Capturing) | Desde document al objetivo |
Objetivo (Target) | En el objetivo mismo |
Burbuja (Bubbling) | Del objetivo a document |
El target es el elemento más bajo en el DOM que recibe el evento.
Es decir, los eventos ocurren en el documento, y van pasando hasta el botón. A continuación, vuelven a hacer el camino en dirección contraria, de vuelta al documento.
Vamos a ver cada una de ellas 👇
Event capturing
Es la primera fase, en la que el evento se propaga desde el documento raíz hacia el elemento objetivo.
En nuestro ejemplo,
- El evento se movería desde el documento raiz, hacia el botón
- Activaría por en medio los manejadores de eventos de todos los elementos intermedios.
Es menos frecuentemente utilizada para capturar eventos que el bubbling.
Event bubbling
En ella el evento se propaga desde el elemento objetivo hacia el documento.
Esta fase ocurre después de la fase del capturing, y permite que los elementos padre manejen los eventos generados en sus elementos hijos.
Es decir, en el ejemplo,
- El evento se movería desde el botón hacía el documento
- Igualmente, activaría por en medio los manejadores de eventos de todos los elementos intermedios.
Esta es la fase por defecto en la mayoría de los navegadores y es la que se utiliza comúnmente en la gestión de eventos.
No todos los eventos soportan bubbling, pero la mayoría lo hace (por ejemplo, click
sí, pero focus
no).
Captura en una u otra fase
Los eventos del DOM pasan siempre por esas fases. Y nosotros podemos elegir en que fase queremos responder al evento.
Para ello disponemos de un parámetro en addEventHandler
.
true
: Captura.false
(o no especificado): Burbuja.
Ejemplo de Event Capturing
Para utilizar capturar un evento durante la fase de propagacion, tenemos que especificar la opción capture: true
al añadir un manejador de eventos.
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Event Capturing</title>
</head>
<body>
<div id="contenedor">
<button id="boton">Haz clic aquí</button>
</div>
<script>
document.getElementById('boton').addEventListener('click', function() {
console.log('Botón clickado');
}, true); // true indica que se debe usar la fase de captura
document.getElementById('contenedor').addEventListener('click', function() {
console.log('Contenedor clickado');
}, true);
document.body.addEventListener('click', function() {
console.log('Body clickado');
}, true);
</script>
</body>
</html>
En este caso,
- Hemos puesto
true
en el tercer parámetro deaddEventHandler
(fase captura) - El mensaje “Body clickado” se imprime primero, seguido por “Contenedor clickado” y finalmente “Botón clickado”
- Es decir, vemos como capturamos el evento cuando este se propaga desde el documento raiz, hacia el botón.
Ejemplo de Event Bubbling
Para utilizar capturar un evento durante la fase de bubbling, tenemos que especificar la opción capture: false
(o no usarla, porque es el valor por defecto) al añadir un manejador de eventos.
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Event Bubbling</title>
</head>
<body>
<div id="contenedor">
<button id="boton">Haz clic aquí</button>
</div>
<script>
document.getElementById('boton').addEventListener('click', function() {
console.log('Botón clickado');
});
document.getElementById('contenedor').addEventListener('click', function() {
console.log('Contenedor clickado');
});
document.body.addEventListener('click', function() {
console.log('Body clickado');
});
</script>
</body>
</html>
En este ejemplo,
- No hemos puesto nada en el tercer parámetro de
addEventHandler
(fase bubbling) - Cuando se hace clic en el botón, el mensaje “Botón clickado” se imprime primero, seguido por “Contenedor clickado” y “Body clickado”
- Es decir, vemos como capturamos el evento cuando este se propaga desde el botón hacia arriba.
Detener la propagación de eventos
En ocasiones, es necesario evitar que un evento se propague a través del DOM, ya sea durante la fase de captura o de burbuja.
Para ello, JavaScript proporciona dos métodos útiles:
Método | Descripción |
---|---|
stopPropagation() | Detiene la propagación del evento, impidiendo que siga viajando hacia los elementos padres. |
| `stopImmediatePropagation()` | Además de detener la propagación, evita que otros manejadores del mismo evento se ejecuten en el mismo elemento. |
Ejemplo de stopPropagation
Usa stopPropagation()
si deseas prevenir que el evento se propague hacia otros elementos del DOM.
<div>
<button id="btn">Haz clic aquí</button>
</div>
document.getElementById("btn").addEventListener("click", function(event) {
event.stopPropagation();
console.log("Botón clickado");
});
document.querySelector("div").addEventListener("click", function() {
console.log("Div clickado");
});
- Al hacer clic en el botón, solo se mostrará en la consola:
Botón clickado
. - El evento no llegará al elemento
<div>
gracias alstopPropagation()
.
Ejemplo de stopImmediatePropagation
Usa stopImmediatePropagation()
si necesitas asegurarte de que otros manejadores en el mismo elemento no se ejecuten.
<button id="boton">Haz clic aquí</button>
document.getElementById("boton").addEventListener("click", function(event) {
console.log("Primer manejador ejecutado");
event.stopImmediatePropagation();
});
document.getElementById("boton").addEventListener("click", function() {
console.log("Este manejador no se ejecutará");
});
- Al hacer clic en el botón, solo se mostrará:
Primer manejador ejecutado
. - El segundo manejador no se ejecutará debido al uso de
stopImmediatePropagation()
.