Event Propagation in JavaScript
Understanding Event Capturing and Event Bubbling
Event Propagation is how JavaScript events travel through the HTML DOM tree. Whenever you click on a link, submit a form, or perform any such action, these events don't just occur at the element at which you performed the action. These travel through the entire HTML DOM tree.
Let's first see an example and then look at the concept in detail.
In the above codepen, let's focus on the 2 div
elements - parent
and child
. child
is nested inside the parent
div. I have attached click event handlers on both the div elements.
Try clicking on the child div and see the output on the right side. Surprised? It says, Parent Clicked!
and Child Clicked!
.
Now try clicking on the parent div. It just says, Parent Clicked!
.
Let's try to understand the mechanics of this! An event doesn't just happen at the target element. It travels up and down the DOM tree. The event propagation can be divided into 3 phases:
- Capture Phase: When a DOM element is clicked, the click event propagates from the
window
down through the DOM tree to thetarget
element. If any of the ancestors of the target element has registered a capture phase event listener, that listener will be executed during this phase. To register an event listener for capturing phase, you need to pass the third parameter astrue
in the addEventListener function.const button = document.getElementById('btn'); button.addEventListener( 'click', () => console.log("Button clicked during capture phase"), true /* this true specifies that this listener would be called during capture phase */ );
- Target Phase: The target phase occurs when the event reaches the target element at which it was generated.
- Bubble Phase: The bubble phase is the exact opposite of the capture phase. The event propagates/bubbles up to the DOM tree from the target element to the window.
const button = document.getElementById('btn'); button.addEventListener( 'click', () => console.log("Button clicked during bubble phase") ); /* if the third parameter is not passed in the listener, then it is executed during the bubble phase */
Note: Not all events bubble up, but most of them do. Events like
blur
,focus
,load
, etc. don't bubble up.
The below example denotes how exactly the event propagation happens. Click on the inner child div and look at the alert text.
Propagation information
During the event propagation, you can access certain info about the event such as what was the element at which the event was performed, at which element the event is at this point of time, and in what phase it is.
- event.target:
event.target
gives you the target element on which the event occurred. The target event is always the most deeply rooted element. - event.currentTarget:
event.currentTarget
gives you the current element on which the listener is being executed. It can be referenced by thethis
keyword as well because the listener is running on it. - event.eventPhase:
event.eventPhase
indicates which phase of the event flow is currently being evaluated. It is an integer that refers to one of the threeEvent
constructor constantsCAPTURING_PHASE
,BUBBLING_PHASE
, andAT_TARGET
.
In the above codepen, you can try adding your own listener to the parent
and click on the child
element to see the target
, currentTarget
, and eventPhase
values. For example:
parent.addEventListener(
'click',
(e) => {
alert("Target: " + e.target.id);
alert("Current Target: " + e.currentTarget.id);
alert("Current Phase: " + e.eventPhase);
}
);
Stopping the Event Propagation
event.stopPropagation():
event.stopPropagation()
stops the propagation of the event to the further event handlers in the path. It can be added to any event listeners registered in any of the phases.In the above codepen, if you modify
window.addEventListener
to addevent.stopPropagation
to the callback function. You won't find any other listeners being executed. Try modifying the codepen with below code:window.addEventListener( 'click', (e) => { alert("Capturing Phase: Window"); e.stopPropagation(); }, true );
If several listeners are attached to the same element for the same event type, then they will be executed in the order in which they are registered.
event.stopPropagation()
won't be able to stop them as they are on the same level (the siblings).event.stopImmediatePropagation():
event.stopImmediatePropagation()
method stops even the sibling event listeners from receiving the event.
Preventing the default event actions
Some of the events like form submission, clicking on a link, etc. have a default action associated with them. You can stop those default actions using event.preventDefault()
.
Note: Preventing the default action won't stop the event propagation. All the 3 phases of event propagation would still take place.