A Beginner-Friendly Intro to JavaScript Event Propagation

A Beginner-Friendly Intro to JavaScript Event Propagation

ยท

5 min read

Introduction

Event propagation is an important concept when dealing with JavaScript events, and it is also crucial in terms of interviews. So, in this article, I'll try to help you understand the concept of Event Propagation.

Event Propagation

To put it in easy words,

The parent elements of a child will also receive events that happen to the children.

The easy words seem tough? Don't worry. Let's understand it with example.

<div class="container">
      <ul>
          <li>
            <button>
                <a href="#" id="item">Some Text</a>
            </button>
        </li>
      </ul>
</div>

Look at the above snippet. We have a a tag nested inside a button which is inside a list item. Suppose we are clicking the link inside the button. The click will generate a click event for the a tag, and it will start to propagate through all the outer elements. To get a better idea about propagation, let's understand the plain English meaning of the word "propagate". It basically means "transmitting from one generation to another" or "moving from one element to others". So, when I say "it will start to propagate through all the outer elements", it means the event will move from one element to the other.

Event Propagation is the order in which the propagation happens. JavaScript has two types of Event Propagation

  • Bubbling
  • Capturing

Bubbling means that the event propagation will start from the item that was clicked and will bubble up to the root node, starting from the closest one.

Capturing is the opposite of bubbling. It starts from the root element and then propagates by capturing all the child element until it reaches the target element that was clicked.

Event Bubbling

It is the default event propagation in JavaScript. As we understand earlier, bubbling starts from the element that was clicked and then it propagates all the way, up to the root. To see it in action, let's add some JavaScript to the HTML that we already have.

let item = document.getElementById('item');
let button = document.querySelector('button');
let list = document.querySelector('li');

// Event Bubbling
item.addEventListener('click', (e) => {
  console.log('Clicked on the Item');
});

button.addEventListener('click', (e) => {
  console.log('Clicked on the Button');
});

list.addEventListener('click', (e) => {
  console.log('Clicked on the li');
});

First of all, we are selecting the item id, button and the li Then, we are adding some event handlers to them. We are listening to the click event here. Callback functions are passed to each event handlers, which will console log the text that we are giving each time the event handler listens.

Now, if we click the a tag inside the button, we will see that our console logs out the output in the exact below order.

Clicked on the Item
Clicked on the Button
Clicked on the li

This is happening because the event starts propagating from the item that was clicked to the outer parents. This will bubble up to the root element. But because we do not have any event handlers associated with those, we can't see it.

Event Capturing

Now let's see an example of event capturing. The addEventListener can take a third parameter. It is a Boolean value which specifies whether the event capturing is true or false. The default value of the parameter is false. This is the reason why event bubbling happens by default. There can be situations where we want to fire the outer elements first before reaching the origin. So, to implement event capturing, we just have to put the third parameter. And our same above code will be like this -

let item = document.getElementById('item');
let button = document.querySelector('button');
let list = document.querySelector('li');

// Capturing
item.addEventListener(
  'click',
  (e) => {
    console.log('Clicked on the Item');
  },
  true
);

button.addEventListener(
  'click',
  (e) => {
    console.log('Clicked on the Button');
  },
  true
);

list.addEventListener(
  'click',
  (e) => {
    console.log('Clicked on the li');
  },
  true
);

Now, clicking the a tag will console log the output in the exact below order,

Clicked on the li
Clicked on the Button
Clicked on the Item

The capturing starts from the root element and then comes to the origin. Because only the topmost li has an event handler, so it starts logging from the li and comes down to the origin which is the item.

The stopPropagation Method

There may be cases where you don't want the propagation to occur. In such cases, we can use the stopPropagation() method into the callback function to stop it. For example, suppose in our first code, we don't want to propagate the event in the third handler. To prevent the propagation, we specify event.stopPropagation() into the callback of the second handler. Reaching the second handler, the propagation would stop.

The Code

let item = document.getElementById('item');
let button = document.querySelector('button');
let list = document.querySelector('li');

// Event Bubbling
item.addEventListener('click', (e) => {
  console.log('Clicked on the Item');
});

button.addEventListener('click', (e) => {
  console.log('Clicked on the Button');
  event.stopPropagation();
});

list.addEventListener('click', (e) => {
  console.log('Clicked on the li');
});

If you click on the button now and check the console log, the output will be,

Clicked on the Item
Clicked on the Button

The event stops propagating after reaching the second handler. The same can also be applied for the capturing.


I hope this article helped you to clarify the JavaScript propagation. If this helped you, click the โค and leave a comment. You can also follow me on twitter.

Also, check this article about Recursion.

Did you find this article valuable?

Support Nemo by becoming a sponsor. Any amount is appreciated!