Event Propagation in JavaScript

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:

Event Propagation.jpg

  1. Capture Phase: When a DOM element is clicked, the click event propagates from the window down through the DOM tree to the target 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 as true 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 */
    );
    
  2. Target Phase: The target phase occurs when the event reaches the target element at which it was generated.
  3. 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.

  1. event.target: event.target gives you the target element on which the event occurred. The target event is always the most deeply rooted element.
  2. event.currentTarget: event.currentTarget gives you the current element on which the listener is being executed. It can be referenced by the this keyword as well because the listener is running on it.
  3. 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 three Event constructor constants CAPTURING_PHASE, BUBBLING_PHASE, and AT_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

  1. 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 add event.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).

  2. 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.

Did you find this article valuable?

Support Shubhaw's Blog by becoming a sponsor. Any amount is appreciated!