Episode 10 of 18

Event Bubbling

Understand event bubbling and event delegation — how events propagate up the DOM tree and how to use this for efficient event handling.

Event Bubbling

When an event fires on an element, it does not just stay there. It bubbles up through the DOM tree — from the target element to its parent, then grandparent, all the way up to the document. This behavior is called event bubbling.

How Bubbling Works

<div id="outer">
    <div id="inner">
        <button id="btn">Click Me</button>
    </div>
</div>

document.getElementById('btn').addEventListener('click', function() {
    console.log('Button clicked');
});
document.getElementById('inner').addEventListener('click', function() {
    console.log('Inner div clicked');
});
document.getElementById('outer').addEventListener('click', function() {
    console.log('Outer div clicked');
});

// Clicking the button outputs:
// "Button clicked"
// "Inner div clicked"
// "Outer div clicked"

The click event fires on the button first, then bubbles up to the inner div, then the outer div. All three listeners fire because the event passes through all of them.

Stopping Propagation

document.getElementById('btn').addEventListener('click', function(e) {
    e.stopPropagation(); // Stops the event from bubbling up
    console.log('Only the button handler runs');
});

e.stopPropagation() prevents the event from continuing up the tree. Only the current handler runs.

Event Delegation

Bubbling enables a powerful pattern called event delegation — instead of adding a listener to every child, add one listener to the parent.

<ul id="ninja-list">
    <li>Ryu</li>
    <li>Ken</li>
    <li>Yoshi</li>
</ul>

// Instead of adding a listener to each <li>...
var list = document.querySelector('#ninja-list');

list.addEventListener('click', function(e) {
    if (e.target.tagName === 'LI') {
        console.log('Clicked:', e.target.textContent);
    }
});

This works because clicks on <li> elements bubble up to the <ul>. You check e.target to see which child was actually clicked. This also works for dynamically added elements.

e.target vs e.currentTarget

PropertyRefers To
e.targetThe element that originally triggered the event
e.currentTargetThe element that the listener is attached to

Key Takeaways

  • Events bubble up from the target element through all ancestors
  • e.stopPropagation() stops the event from bubbling further
  • Event delegation attaches one listener to a parent instead of many to children
  • Delegation works for dynamically added elements and improves performance