DOM events do not simply trigger in isolation on a single element. When an action occurs, the event travels through a structured three-phase lifecycle known as the DOM Event Flow: the Capturing Phase, the Target Phase, and the Bubbling Phase.
1. The 3 Phases of Event Propagation
Output
1 PHASE 1: CAPTURING (Downwards)
2 Window ───────────────┐
3 │ │
4 Document │
5 │ │
6 <html> │
7 │ │
8 <body> │
9 │ │
10 <div id="wrapper"> │
11 │ ▼
12 │ PHASE 2: TARGET
13 │ <button id="btn">Click Me</button>
14 │ ▲
15 │ │
16 PHASE 3: BUBBLING (Upwards)
1
Capturing Phase (Trickling): The event starts at window and travels down through document, html, body, and parent containers until it reaches the target.
2
Target Phase: The event reaches the originating element (event.target).
3
Bubbling Phase: The event travels back upwards from the target through all ancestors until reaching window.
Default Behavior: By default, addEventListener() registers listeners for the Bubbling Phase unless explicitly specified otherwise.
2. Capturing in Practice
To listen during the Capturing Phase, pass { capture: true } or simply true as the third parameter:
4// Prevent click on modal content from closing the backdrop overlay
5 e.stopPropagation();
6 console.log('Clicked inside modal content');
7});
event.stopImmediatePropagation()
Not only halts bubbling to parent nodes, but also prevents other listeners on the very same element from firing:
JavaScript
1button.addEventListener('click',(e)=>{
2 console.log('Primary handler');
3 e.stopImmediatePropagation();
4});
5
6button.addEventListener('click',()=>{
7 console.log('Secondary handler');// NEVER EXECUTED!
8});
4. Event Delegation: The Power of Bubbling
Because events bubble up through parent nodes, you do not need to attach separate listeners to hundreds of child elements. Instead, attach a single listener to the parent element.