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

Yasar ArafathYasar Arafath
4 min read

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, and preventDefault

  • 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

  1. Capturing Phase

  2. Target Phase

  3. 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

ConceptDescription
Event PropagationThe journey of an event through capture → target → bubble
CapturingTop-down phase before reaching target
BubblingBottom-up phase after reaching target
stopPropagationStops the event from propagating further
stopImmediatePropagationStops other handlers on the same element
preventDefaultStops the browser’s default behavior for an event
Event DelegationUsing 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 vs e.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.

2
Subscribe to my newsletter

Read articles from Yasar Arafath directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Yasar Arafath
Yasar Arafath