The event model in JavaScript is based on a propagation system that allows events to travel from parents to children and vice versa through the document structure.
It’s important to understand this model to control the order in which DOM elements respond to an event.
For example, when we click a button inside a form, we are clicking both the form and the button.

Which element do you want to react to the click first? And how do we tell JavaScript? This is where the event propagation model comes in.
This model has three phases:
| Phase | Execution Order |
|---|---|
| Capturing | From document to the target |
| Target | On the target itself |
| Bubbling | From the target to document |
The target is the lowest element in the DOM that receives the event.
That is, events occur in the document and travel down to the button. Then, they make the journey back in the opposite direction, back to the document.
Let’s look at each one 👇
Event capturing
This is the first phase, where the event propagates from the root document towards the target element.

In our example,
- The event would move from the root document towards the button
- It would trigger the event handlers of all intermediate elements along the way.
It is less frequently used for capturing events than bubbling.
Event bubbling
In this phase, the event propagates from the target element towards the document.
This phase occurs after the capturing phase and allows parent elements to handle events generated by their child elements.

That is, in the example,
- The event would move from the button towards the document
- Similarly, it would trigger the event handlers of all intermediate elements along the way.
This is the default phase in most browsers and is the one commonly used in event handling.
Not all events support bubbling, but most do (for example, click does, but focus does not).
Capturing in one phase or the other
DOM events always go through these phases. And we can choose in which phase we want to respond to the event.
For this, we have a parameter in addEventHandler.
true: Capture.false(or unspecified): Bubble.
Event Capturing Example
To capture an event during the capturing phase, we must specify the capture: true option when adding an event handler.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Event Capturing</title>
</head>
<body>
<div id="container">
<button id="button">Click here</button>
</div>
<script>
document.getElementById('button').addEventListener('click', function() {
console.log('Button clicked');
}, true); // true indicates that the capturing phase should be used
document.getElementById('container').addEventListener('click', function() {
console.log('Container clicked');
}, true);
document.body.addEventListener('click', function() {
console.log('Body clicked');
}, true);
</script>
</body>
</html>
In this case,
- We set
truein the third parameter ofaddEventHandler(capture phase) - The message “Body clickado” is printed first, followed by “Contenedor clickado” and finally “Botón clickado”
- That is, we see how we capture the event as it propagates from the root document down to the button.
Event Bubbling Example
To capture an event during the bubbling phase, we must specify the capture: false option (or not use it, as it’s the default) when adding an event handler.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Event Bubbling</title>
</head>
<body>
<div id="container">
<button id="button">Click here</button>
</div>
<script>
document.getElementById('button').addEventListener('click', function() {
console.log('Button clicked');
});
document.getElementById('container').addEventListener('click', function() {
console.log('Container clicked');
});
document.body.addEventListener('click', function() {
console.log('Body clicked');
});
</script>
</body>
</html>
In this example,
- We didn’t specify anything in the third parameter of
addEventHandler(bubbling phase) - When clicking the button, the message “Botón clickado” is printed first, followed by “Contenedor clickado” and “Body clickado”
- That is, we see how we capture the event as it propagates from the button upwards.
Stopping event propagation
Sometimes it’s necessary to prevent an event from propagating through the DOM, whether during the capture or bubble phase.
For this, JavaScript provides two useful methods:
| Method | Description |
|---|---|
stopPropagation() | Stops the event propagation, preventing it from traveling further to parent elements. |
| `stopImmediatePropagation()` | In addition to stopping propagation, it prevents other handlers for the same event from executing on the same element. |
stopPropagation Example
Use stopPropagation() if you want to prevent the event from propagating to other DOM elements.
<div>
<button id="btn">Click here</button>
</div>
document.getElementById("btn").addEventListener("click", function(event) {
event.stopPropagation();
console.log("Button clicked");
});
document.querySelector("div").addEventListener("click", function() {
console.log("Div clicked");
});
- When clicking the button, only this will be shown in the console:
Botón clickado. - The event will not reach the
<div>element thanks tostopPropagation().
stopImmediatePropagation Example
Use stopImmediatePropagation() if you need to ensure that other handlers on the same element do not execute.
<button id="button">Click here</button>
document.getElementById("button").addEventListener("click", function(event) {
console.log("First handler executed");
event.stopImmediatePropagation();
});
document.getElementById("button").addEventListener("click", function() {
console.log("This handler will not execute");
});
- When clicking the button, only this will be shown:
Primer manejador ejecutado. - The second handler will not execute due to the use of
stopImmediatePropagation().
