<img alt="Event Bubbling in JavaScript The Hidden Key to Dynamic Web Interactions" data- data-src="https://kirelos.com/wp-content/uploads/2023/11/echo/Event-Bubbling-in-JavaScript-The-Hidden-Key-to-Dynamic-Web-Interactions-800×420.jpg" data- decoding="async" height="420" src="data:image/svg xml,” width=”800″>

When I was still learning the ropes of web development, one of the surprising and intriguing behaviors I encountered was event bubbling. At first, it seemed unusual, but when you think about event bubbling, you’ll see it makes so much sense. As a web developer, you’re bound to encounter event bubbling. So what is event bubbling?

To provide users with the ability to interact with web pages, JavaScript relies on events. An event refers to occurrences or actions that can be detected and responded to by the code you write. Examples of events include mouse clicks, key presses, and form submission, among others.

JavaScript uses event listeners to detect and respond to events. An event listener is a function that listens for or waits for a specific event to occur on a page. For instance, a click event on a button. When an event listener detects the event it is listening for, it responds by executing code associated with the event. This entire process of capturing and responding to events is called event handling.

Now, imagine we have three elements on a page: a div, a span, and a button. The button element is nested inside the span element, and the span element is nested inside the div. An illustration of this is shown below:

<img alt="div-span-button-1" data- data-src="https://kirelos.com/wp-content/uploads/2023/11/echo/div-span-button-1.png" data- decoding="async" height="377" src="data:image/svg xml,” width=”669″>

Assuming each of these elements has an event listener that listens for a click event and prints to the console when clicked, what happens when you click on the button?

To test this yourself, create a folder, and in the folder, create an HTML file called index.html, a CSS file called style.css, and a JavaScript file called app.js.

In the HTML file, add the following code:





  Event bubbling
  



  

In the CSS file, add the following code to style the div and the span element.

div {
  border: 2px solid black;
  background-color: orange;
  padding: 30px;
  width: 400px;
}

span {
  display: inline-block;
  background-color: cyan;
  height: 100px;
  width: 200px;
  margin: 10px;
  padding: 20px;
  border: 2px solid black;
}

In the JavaScript file, add the following code, which adds event listeners to the div, span, and button elements. The event listers are all listening for a click event.

const div = document.querySelector('div');
div.addEventListener('click', () => {
  console.log("You've clicked a div element")
})

const span = document.querySelector('span');
span.addEventListener('click', () => {
  console.log("You've clicked a span element")
})

const button = document.querySelector('button');
button.addEventListener('click', () => {
  console.log("You've clicked a button")
})

Now open the HTML file in a browser. Inspect the page, then click on the button on the page. What do you notice? The output of clicking on the button is shown below:

<img alt="event-bubbling" data- data-src="https://kirelos.com/wp-content/uploads/2023/11/echo/event-bubbling.png" data- decoding="async" height="514" src="data:image/svg xml,” width=”913″>

Clicking on the button triggers the event listener listening for a click event on the button. However, the event listeners on the span and div elements are also triggered. Why is this the case, you might ask.

Clicking on the button triggers the event listener attached to the button, which prints to the console. However, since the button is nested inside a span element, clicking on the button means that technically, we are also clicking on the span element, and therefore, its event listener is triggered.

Since the span element is also nested inside the div element, clicking on the span element also means that the div element is also clicked, and as a result, its event listener is also triggered. This is event bubbling.

Event Bubbling

Event bubbling is the process by which an event triggered in a nested set of HTML elements is propagated or bubbles up from the innermost element where it was triggered and travels up the DOM tree to the root element, triggering all event listeners listening for that event.

The event listeners are triggered in a specific order which matches how the event bubbles or propagates up the DOM tree. Consider the DOM tree shown below, which represents the structure of the HTML used in this article.

<img alt="click-event-bubbling" data- data-src="https://kirelos.com/wp-content/uploads/2023/11/echo/click-event-bubbling.png" data- decoding="async" height="540" src="data:image/svg xml,” width=”960″>
A click event bubbling up the DOM tree

The DOM tree shows a button, nested inside a span, which is nested inside a div, that is nested inside the body and the body is nested inside the HTML element. Since the elements have been nested inside each other when you click on the button, the click event will trigger the event listener attached to the button.

However, since the elements are nested, the event will move up the DOM tree(bubble up) to the span element, then the div, then the body, and finally the HTML element, triggering all event listeners listening for a click event in that order.

This is why the event listener attached to the span and div elements is executed. If we had event listeners listening for a click on the body and the HTML element, they would have also been triggered.

The DOM node where an event occurs is called a target. In our case, since the click happens on the button, the button element is the event target.

How To Stop Event Bubbling

To stop an event from bubbling up the DOM, we use a method called stopPropagation() which is available on the event object. Consider the code sample below, which we used to add an event listener to the button element.

const button = document.querySelector('button');
button.addEventListener('click', () => {
  console.log("You've clicked a button");
})

The code leads to an event bubbling up the DOM tree when a user clicks on the button. To prevent event bubbling, we call the stopPropagation() method as shown below:

const button = document.querySelector('button');
button.addEventListener('click', (e) => {
  console.log("You've clicked a button");
  e.stopPropagation();
})

An event handler is the function that is executed when a button is clicked. An event listener automatically passes an event object to an event handler. In our case, this event object is represented by the variable name e, which is passed as a parameter in the event handler.

This event object,e, contains information about an event and also gives us access to a variety of properties and methods relating to events. One such method is stopPropagation(), which is used to prevent event bubbling. Calling stopPropagation() in the button’s event listener prevents an event from bubbling up the DOM tree from the button element.

The result of clicking the button after adding the stopPropagation() method is shown below:

<img alt="stop-propagation" data- data-src="https://kirelos.com/wp-content/uploads/2023/11/echo/stop-propagation.png" data- decoding="async" height="514" src="data:image/svg xml,” width=”913″>

We use stopPropagation() to prevent an event from bubbling up from the element we use it on. For instance, if we wanted the click event to bubble up from the button element up to the span element and not bubble further up the DOM tree, we’d use stopPropagation() on the span’s event listener.

Event Capturing

Event capturing is the opposite of event bubbling. In event capturing, an event trickles down from the outermost element to the target element as illustrated below:

<img alt="event-capturing" data- data-src="https://kirelos.com/wp-content/uploads/2023/11/echo/event-capturing.png" data- decoding="async" height="245" src="data:image/svg xml,” width=”435″>
Click event trickling down to the target element because of event capturing

For instance, in our case, when you click on the button element, in event capturing, the event listeners on the div element will be the first to be triggered. The listeners on the span element follow and finally, the listeners on the target element will be triggered.

Event bubbling is, however, the default way in which events are propagated in the Document Object Model(DOM). To change the default behavior from event bubbling to event capturing, we pass in a third argument to our event listeners to set event capturing to true. If you don’t pass in a third argument to the event listeners, event capturing is set to false.

Consider the event listener below:

div.addEventListener('click', () => {
  console.log("You've clicked a div element")
})

Since it has no third argument, capturing is set to false. To set capturing to true, we pass in a third argument, the boolean value true, which sets capturing to true.

div.addEventListener('click', () => {
  console.log("You've clicked a div element")
}, true)

Alternatively, you could pass in an object that sets capture to true, as shown below:

div.addEventListener('click', () => {
  console.log("You've clicked a div element")
}, {capture: true})

To test out event capturing, in your JavaScript file, add a third argument to all your event listeners as shown:

const div = document.querySelector('div');
div.addEventListener('click', () => {
  console.log("You've clicked a div element")
}, true)

const span = document.querySelector('span');
span.addEventListener('click', (e) => {
  console.log("You've clicked a span element")
}, true)

const button = document.querySelector('button');
button.addEventListener('click', () => {
  console.log("You've clicked a button");
}, true)

Now open your browser and click on the button element. You should get such an output:

<img alt="event-capturing-1" data- data-src="https://kirelos.com/wp-content/uploads/2023/11/echo/event-capturing-1.png" data- decoding="async" height="516" src="data:image/svg xml,” width=”918″>

Notice that unlike in event bubbling, where the output from the button was printed out first, in event capturing, the first output is from the outermost element, the div.

Event bubbling and event capturing are the main ways through which events are propagated in the DOM. However, event bubbling is what is commonly used to propagate events.

Event Delegation

Event delegation is a design pattern where a single event listener is attached to a common parent element for instance a

    element, instead of having event listeners on each of the child elements. Events on child elements then bubble up to the parent element, where they are handled by the event handler on the parent element.

    Therefore, in a case where we have a parent element with child elements inside it, we only add an event listener to the parent element. This event handler will handle all the events in the child elements.

    You might be wondering, how will the parent element know which child element was clicked. As mentioned before, an event listener passes an event object to an event handler. This event object has methods and properties that offer information about a particular event. One of the properties in the event object is the target property. The target property points to the specific HTML element where an event occurred.

    For example, if we have an unordered list with list items and we attach an event listener to the

      element when an event occurs on a list item, the target property in the event object will point to the specific list item where the event occurred. 

      To see event delegation in action, add the following HTML code to the existing HTML file:

      • Toyota
      • Subaru
      • Honda
      • Hyundai
      • Chevrolet
      • Kia

      Add the following JavaScript code to use event delegation to use a single event listener on a parent element to listen for events in child elements:

      const ul = document.querySelector('ul');
      ul.addEventListener('click', (e) => {
        // target element
        targetElement = e.target
        // log out the content of the target element
        console.log(targetElement.textContent)
      })

      Now open the browser and click on any item in the list. The contents of the element should be printed to the console as shown below:

      <img alt="event-delagation" data- data-src="https://kirelos.com/wp-content/uploads/2023/11/echo/event-delagation.png" data- decoding="async" height="517" src="data:image/svg xml,” width=”918″>

      Using a single event listener, we can handle events in all the child elements. Having so many event listeners on a page affects its performance, consumes more memory, and leads to slow loading and rending of pages.

      Event delegation allows us to avoid all this by minimizing the number of event listeners we need to use on a page. Event delegation relies on event bubbling. Therefore, we can say event bubbling can help in optimizing the performance of web pages.

      Tips for Efficient Event Handling

      As a developer, when working with events in the document object model, consider using event delegation rather than having a lot of event listeners on elements on your page.

      <img alt="Factors-That-Contribute-to-Website-Performance-" data- data-src="https://kirelos.com/wp-content/uploads/2023/11/echo/Factors-That-Contribute-to-Website-Performance-.png" data- decoding="async" height="596" src="data:image/svg xml,” width=”1060″>

      When using event delegation, remember to attach an event listener to the closest common ancestor of the child elements that need event handling. This helps in optimizing event bubbling and also minimizes the path an event has to travel before it is handled.

      When handling events, use the event object that is provided by the event listener to your advantage. The event object contains properties such as target, which come in handy in handling events.

      To have more performant websites, avoid excessive DOM manipulation. Having events that trigger frequent DOM manipulations can negatively affect the performance of your website.

      Finally, in the case of nested elements, be very cautious when attaching nested event listeners to the elements. This can not only affect performance but will make event handling very complex, and your code will be hard to maintain.

      Conclusion

      Events are a powerful tool in JavaScript. Event bubbling, event capturing, and event delegating are important tools in handling events in JavaScript. As a web developer, use the article to familiarize yourself with concepts so that you can build more interactive, dynamic, and performant websites and applications.