Event Propogation in js
Event Propagation in JavaScript
Mastering the 3 phases of DOM events: Capturing, Target, and Bubbling, plus Event Delegation.
The Deep Sea Submarine Dive and Surface
Event Propagation defines how events travel through the DOM hierarchy. Mastering bubbling and capturing allows developers to implement high-performance patterns like Event Delegation.
1. Capturing Phase (Trickling)
The event journeys from `Window` down through nested ancestor nodes until it reaches the parent of the target element. Enabled by setting `{ capture: true }`.
2. Target Phase
The event reaches the exact element on which the interaction occurred (`e.target`).
3. Bubbling Phase
The event travels upwards from the target element back up to `Window`. This is the default phase for `element.addEventListener("click", fn)`.
Stopping Propagation
`e.stopPropagation()` halts upward/downward travel through the DOM. `e.stopImmediatePropagation()` also prevents other listeners attached to the same element from running.
Event Delegation Pattern
Instead of attaching 1,000 listeners to 1,000 list items, attach 1 listener to their parent container and inspect `e.target` to identify which item was clicked.
Implementing Event Delegation and distinguishing e.target vs e.currentTarget:
// Simulating Event Delegation on a dynamic list
const listContainer = {
handleEvent(e) {
// e.target: the actual clicked DOM element (e.g., button inside li)
// e.currentTarget: the element with the listener (listContainer)
console.log("Target clicked tag:", e.target.tagName);
console.log("CurrentTarget listener tag:", e.currentTarget.tagName);
if (e.target.dataset.action === "delete") {
console.log("Delete item with ID:", e.target.dataset.id);
}
}
};
const fakeEvent = {
target: { tagName: "BUTTON", dataset: { action: "delete", id: "item-42" } },
currentTarget: { tagName: "UL" }
};
listContainer.handleEvent(fakeEvent);
Target clicked tag: BUTTON
CurrentTarget listener tag: UL
Delete item with ID: item-42
Confusing e.target with e.currentTarget
The Risk: `e.target` is the deepest element that triggered the click (e.g. an `` icon inside a button), whereas `e.currentTarget` is the element holding the event listener.
The Fix: Use `e.target.closest("button")` when dealing with nested icons inside clickable elements.
Overusing stopPropagation()
The Risk: Calling `e.stopPropagation()` breaks page-wide click trackers, analytics tools, and dropdown dismiss listeners.
The Fix: Rely on checking element IDs or classes instead of violently cutting off event propagation.
- ✓ 3 phases: Capturing (down) → Target → Bubbling (up).
- ✓ Default listeners run on the Bubbling phase (`capture: false`).
- ✓ `e.target` = what was clicked; `e.currentTarget` = where the listener is attached.
- ✓ Event Delegation attaches one listener on a parent to handle unlimited dynamic children.
- ✓ Some events do NOT bubble (e.g., `focus`, `blur`, `mouseenter`, `mouseleave`).