Event Propagation in JavaScript: How One Line of Code Fixed My Bug
Yesterday, I encountered an interesting bug while working on a UI component.
There was a clickable parent container with a clickable child element inside it. The requirement was straightforward:
- Clicking the parent should open Modal A.
- Clicking the child should open Modal B.
- Clicking the child should never open both modals.
But that wasn't what happened.
Whenever I clicked the child element, both modals opened at the same time.
Initially, it looked like a component or state management issue. After investigating, I found the actual culprit: event propagation.
The fix was just one line of code:
onClick={(e) => {
e.stopPropagation();
// rest of the code to open an AI chat window
}}
One line fixed the bug, but understanding why it worked was the interesting part.
Let's break it down.
What Is Event Propagation
Event propagation describes how an event travels through the DOM when something happens, such as clicking a button.
Consider this simple HTML:
<div id="parent">
<button id="child">Click me</button>
</div>
The button is inside the parent container.
Now, suppose both elements have click handlers:
const parent = document.querySelector("#parent");
const child = document.querySelector("#child");
parent.addEventListener("click", () => {
console.log("Parent clicked");
});
child.addEventListener("click", () => {
console.log("Child clicked");
});
What happens when you click the button?
You might expect only Child clicked to appear.
However, the console outputs:
Child clicked
Parent clicked
Why?
Because clicking the child doesn't automatically prevent the event from reaching its parent.
To understand this behavior, we need to look at the three phases of event propagation.
The Three Phases of Event Propagation
Event propagation has three phases:
- Capturing
- Target
- Bubbling
Let's understand each one.
Phase 1: Capturing
The capturing phase begins at the top of the event path and travels down toward the element that was clicked.
For example, the path might look like this:
Document
↓
Body
↓
Parent
↓
Child ← Target