Skip to Content
Course content

83: Event Bubbling and Capturing

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Imagine you work in a corporate office. You're a junior developer, and your boss is the manager, and their boss is the director. If a client sends you a direct email with a complaint, you're the first person to see it. But in a corporate environment, that email doesn't just stay with you. You might handle it, but the manager is CC'd, and the director is also looped in. The "event" (the complaint) starts at the bottom and ripples upward through the chain of command.

In JavaScript, this is exactly how events work. When you click a button, you aren't just clicking that button; you're also clicking the div that contains the button, the section that contains the div, and eventually the body and the window. The browser handles this in two directions: Bubbling and Capturing.

The Default Ride Upwards

By default, almost every event you'll use in JavaScript "bubbles." This means the event starts at the most specific element (the target) and moves up the DOM tree to the root. I've seen countless developers get frustrated when they put a click listener on a card and a click listener on a button inside that card, only to find that clicking the button triggers both functions.

<div id="product-card" style="padding: 20px; background: lightgrey;">
  Product Name
  <button id="buy-btn">Buy Now</button>
</div>

<script>
const card = document.querySelector('#product-card');
const btn = document.querySelector('#buy-btn');

card.addEventListener('click', () => {
  console.log('Card clicked!');
});

btn.addEventListener('click', () => {
  console.log('Button clicked!');
});
</script>

If you run this and click the "Buy Now" button, your console will show "Button clicked!" followed immediately by "Card clicked!". The event bubbled up from the button to the card. This is actually incredibly useful for something called "Event Delegation," where you listen for events on a parent rather than attaching a hundred listeners to a hundred different children.

Intercepting the Event on the Way Down

Now, capturing is the opposite. It's like the director seeing the email before it ever reaches your inbox. The event starts at the window and trickles down to the target. You rarely use this in day-to-day coding, but it's a lifesaver when you need to intercept an event before it reaches a child element.

To enable capturing, you pass a third argument—a boolean true—to addEventListener. If you don't provide this, it defaults to false (bubbling).

// This listener will fire BEFORE the button's own listener
card.addEventListener('click', () => {
  console.log('Card intercepted the click first!');
}, true); 

Cutting the Signal

Sometimes, you just want the event to stop. If you're building a dropdown menu, you might have a "Delete" button inside a clickable row. You don't want the "Row Selected" event to fire just because the user wanted to delete the item. This is where event.stopPropagation() comes in.

I like to think of this as the "emergency brake." It tells the browser, "I've handled this; don't let anyone else in the chain know about it."

btn.addEventListener('click', (event) => {
  event.stopPropagation(); 
  console.log('Only the button will fire. The card will never know.');
});

A quick word of caution: use stopPropagation() sparingly. If you kill the event bubble, other global scripts or analytics tools that are listening for clicks at the document level might stop working, and you'll be left scratching your head wondering why your tracking pixels aren't firing.




📋 Practical Task

Build a Nested Category Filter

Your goal is to create a simple category navigation system where clicking a specific sub-category doesn't trigger the main category's "General Search" function.

Requirements:

  • Create a div with the ID category-container and a button inside it with the ID sub-category-btn.
  • Add a click event listener to category-container that logs: "Performing general category search..."
  • Add a click event listener to sub-category-btn that logs: "Filtering by specific sub-category!"
  • Implement the logic so that when the sub-category-btn is clicked, only its own message appears in the console, preventing the category-container listener from firing.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.