DEV Community

Afan Khan
Afan Khan

Posted on • Originally published at javascript.plainenglish.io

Event Propagation & Delegation: Bubbling and Capturing in JavaScript DOM

JavaScript DOM events have specific phases, from clicking an element on a website to the JavaScript responding with a surprise. Most developers don't know how the browser handles events in its engine with the DOM tree.

If taken advantage of, they can reduce multiple LoC and efforts of the developer alongside the client-side browser. Only a few people are aware of these techniques. You can be one of them.


I launched a new free eBook!

The eBook is a 4-step framework to build projects and learn skills that enable you to attain practical experience in your field and become a developer whom recruiters and clients cannot say "No" during the discovery call.

Click here to get your copy


Capturing & Target Phase

We will use the following HTML code during this article.

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Event Propogation and Delegation</title>

    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <section class="main">
      <p class="main-p">Click here 1</p>
      <p class="main-p">Click here 2</p>
      <p class="main-p">Click here 3</p>
    </section>

    <script src="script.js"></script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

What do you expect the DOM to do when you click on one of those <p> elements? It creates a click event! However, the click event does not occur directly on the target element or the <p> tag. The actual event gets generated at the root of the HTML document. The Document object represents the root.

GIF Animation demonstrating the Capturing Phase by Afan Khan

This is the Capturing Phase. The click event object travels from the root of the DOM tree to the target element. It touches each parent element in the DOM tree before reaching its target during leisure travel because it gets generated at the root. The target phase begins when the event object arrives at the target element.

During the target phase, the event handler executes the callback function on the target element after the object reaches the target element.

These event handlers or listeners wait for specific events to occur on particular elements of the HTML DOM tree, and they react once that happens with a callback function alongside the default event object.

Bubbling Phase

In the bubbling phase, the event object returns to the document root after the execution of the callback function. The event comes from the root and returns to the root.

Most developers leverage this bubbling phase to attach event listeners to parent elements instead of multiple child elements.

GIF Animation demonstrating the Bubbling Phase by Afan Khan

The events bubble up from the target to the document root element. The event passes through each parent element, not through any sibling elements, during its reincarnation to the root.

Cool, but how the heck is this beneficial?

It becomes beneficial because the Bubbling and Capturing phases allow events to pass through their parent elements. If we attach event listeners to parent elements instead of the target elements (child elements), we can perform the same operation with fewer LoC. These events precisely propagate from one end to another.

Before reading this article, you used a forEach loop to iterate through each element of a specific class and attached event listeners to all of them, as shown in the following example.

const pTag = document.querySelectorAll(".main-p");

pTag.forEach((element) => {
  element.addEventListener("click", () => {
    element.classList.toggle("p-color-change");
  });
});
Enter fullscreen mode Exit fullscreen mode

Code Snippet Output - Without Bubbling

But this is not viable. The loop takes ample time, and adding event listeners to all elements is inefficient. Therefore, we directly attach it to the parent element. In our case, the parent element is the <section> tag.

const section = document.querySelector(".main");

section.addEventListener("click", (e) => {
  const target = e.target;

  if (target.classList.contains("main-p")) {
    target.classList.toggle("p-color-change");
  }
});
Enter fullscreen mode Exit fullscreen mode

As stated earlier, each event creates an object with many intricate details. Even the click event generates a click object that you can use by passing it as the first argument to the callback function of the event listener.

The target property within the event object returns the HTML element that experienced the event. I am storing that HTML element in a variable for later use. After that, I verify whether the target element is my

tag with a specific class. If yes, it will change the colour. Otherwise, it will ignore it.

The if-statements are required because we attached the event listener to the parent element. There may be other elements in that parent element. I want the colour to change for any element except the

tag. Hence, I require the if-statement.

Output with Bubbling

We achieved the same outcome with more efficiency. If you noticed, we use the Event object as a parameter to the callback function in the addEventListener() method. This object contains the properties of a specific event and travels to the target element.

Otherwise, you would attach an event listener to each parent element for the same callback function. You can directly connect the event listener to the parent element. The event object travels from the bottom to the top and vice versa as if the event had occurred on those elements instead of the target element.

We can take leverage of this behaviour. Events can only be handled in the target and bubbling phases by default. But we can also set up a different approach to operate event listeners in the capturing phase with an extra parameter in the addEventListener() method. However, we avoid doing that and prefer the Bubbling technique, as shown in the code snippet above.

By the way, some events can't have Capturing and Bubbling phases. They directly opt for the target phase. However, most of the events must use the Capturing and Bubbling technique. If you notice that an event acts suspiciously to this approach, it could be because it is not supported yet.

Summary

This was the process of Event Delegation because we were delegating the responsibility of the event listeners from the child elements to the parent elements with the concepts of Event Propagation.

You can also perform Event Propagation without delegation, but I only do that to explain alternative solutions to students. We can add a third boolean parameter to addEventListener() if we want them to invoke the callback functions during the capturing phase instead of the bubbling phase. By default, it is set to false as developers prefer using Bubbling.

Bubbling and Capturing are here because of historical reasons when different browsers implemented identical, but not the same, versions of JavaScript. It was before ECMA Specifications.

To summarize, the Event Propagation process has three different phases — Capturing, Bubbling, and Target. Each of these phases appears at the different DOM event reaction stages. They allow us to effectively apply event listeners to multiple elements concurrently without the requirement of loops and 100s of listeners.

Top comments (0)