Deep Dive into JavaScript Events: Bubbling, Propagation, Capturing & More


JavaScript events are at the core of interactive web applications. Whether you're clicking a button, submitting a form, or typing into an input field, you're working with the DOM event model. But beneath these seemingly simple interactions lies a powerful and intricate system known as event propagation.
In this blog, we’ll take a deep dive into
What is event propagation?
The difference between event bubbling and capturing
How to control event flow with
stopPropagation
,stopImmediatePropagation
, andpreventDefault
Real-world use cases and best practices
What is Event Propagation?
When an event occurs in the DOM, such as a user clicking on a button inside a form, it doesn’t just affect the button alone. The event travels through a phased journey involving
Capturing Phase
Target Phase
Bubbling Phase
This journey is known as event propagation.
Phase 1: Capturing (Trickling Down)
Also called the capture phase, this is the first phase in the event propagation model. It starts from the window
object and travels down the DOM tree toward the event target.
element.addEventListener('click', handler, true); // third parameter true = capture
Events registered with the
capture
flag are triggered before those in the bubbling phase.This is useful when parent elements need to intercept an event before it reaches the target.
Phase 2: Target
This is where the event reaches the actual target element—the one that triggered the event. Handlers attached directly to the target will fire here, regardless of whether they’re in the capture or bubble phase.
Phase 3: Bubbling (Trickling Up)
After the event reaches the target, it begins to bubble up the DOM tree, from the target's parent all the way up to the window
.
element.addEventListener('click', handler); // bubbling by default
Bubbling is the most commonly used phase in event handling.
All major browsers support bubbling by default for most event types (like
click
,input
,submit
, etc.).
Controlling Propagation
Sometimes you don’t want the event to propagate further. JavaScript provides several methods to control this behaviour.
event.stopPropagation()
Stops the event from bubbling or capturing further up/down the DOM.
element.addEventListener('click', function (e) {
e.stopPropagation();
});
Use case:
- Prevent a modal click from closing the modal if a background layer is listening for clicks to close it.
event.stopImmediatePropagation()
Stops the event entirely, including:
Bubbling
Capturing
Execution of other handlers on the same element
element.addEventListener('click', function (e) {
e.stopImmediatePropagation();
});
Use case:
- You want to ensure no other event handlers on this element execute after the current one.
event.preventDefault()
Prevents the browser’s default behavior associated with an event.
element.addEventListener('submit', function (e) {
e.preventDefault(); // Prevents form submission
});
Use case:
- Prevent link navigation, form submission, checkbox selection, etc., when you want full control over what happens.
Example: Bubbling in Action
<div id="outer">
<div id="inner">
<button id="btn">Click me</button>
</div>
</div>
document.getElementById('outer').addEventListener('click', () => {
console.log('Outer clicked');
});
document.getElementById('inner').addEventListener('click', () => {
console.log('Inner clicked');
});
document.getElementById('btn').addEventListener('click', () => {
console.log('Button clicked');
});
Output when clicking the button:
Button clicked
Inner clicked
Outer clicked
Because the event bubbles up from #btn
→ #inner
→ #outer
.
Event Delegation
Event delegation is a pattern that uses event bubbling to handle events at a higher level in the DOM, rather than attaching event listeners to each child.
document.getElementById('list').addEventListener('click', function (e) {
if (e.target.tagName === 'LI') {
console.log(`Clicked on item: ${e.target.textContent}`);
}
});
Use case:
- Handling clicks on dynamically added elements like list items, buttons, etc.
Summary Table
Concept | Description |
Event Propagation | The journey of an event through capture → target → bubble |
Capturing | Top-down phase before reaching target |
Bubbling | Bottom-up phase after reaching target |
stopPropagation | Stops the event from propagating further |
stopImmediatePropagation | Stops other handlers on the same element |
preventDefault | Stops the browser’s default behavior for an event |
Event Delegation | Using bubbling to handle child events on a parent |
Best Practices
Prefer bubbling (default) unless capturing is specifically needed.
Use delegation for dynamic or large numbers of elements.
Always check
e.target
vse.currentTarget
:e.target
is the actual clicked element.e.currentTarget
is the element the listener is attached to.
Avoid excessive use of
stopPropagation()
—let the event bubble unless there's a clear reason not to.
Final Thoughts
Understanding the event model is crucial to writing efficient, bug-free, and scalable front-end code. Mastering event propagation lets you handle complex UI behavior, optimise performance with delegation, and prevent unwanted side effects.
Subscribe to my newsletter
Read articles from Yasar Arafath directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
