What Is Event Delegation, Capturing, and Bubbling in a Browser?

January 20, 2023

☕️ Support Us
Your support will help us to continue to provide quality content.👉 Buy Me a Coffee

When the user interacts with the browser, various events are triggered, such as clicks and scrolls. We can handle these events through JavaScript event handlers. Event delegation is an important pattern you can use when you handle an event. Hence, it is one of the most common interview questions.

If you are not familiar with event delegation, capturing, and bubbling in the browser, make sure you learn them before going to an interview.

Event delegation

Event delegation is useful when we want to add the same event listeners and handlers to a group of child elements. When we have many identical elements with similar behaviors, instead of adding handlers to each element, we can directly add handlers to the parent layer. With this approach, we use event.target to know which element actually has an event, and then handle the event.

Adding listeners and handlers in the parent layer, and then delegated to child elements, is what we call event delegation. The advantage of this is that we don't need to add a handler to each element, such as each button, which can reduce memory consumption; this also makes our architecture more flexible, and elements can be added or removed at any time. You can also write less code to improve readability.

For example (note: This example comes from MDN), if you want to add handler to every div in a long list, instead of adding every child element, we can add it directly to the parent layer, even if there are hundreds or thousands of child elements.

<div id="container">
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
</div>;

const container = document.querySelector("#container");
container.addEventListener(
  "click",
  (event) => (event.target.style.backgroundColor = bgChange())
);

Event capturing

The event delegation can happen because of the event capturing and bubbling mechanism behind it. Generally speaking, when an event is triggered, it will first enter the capturing phase, then reach the event target, and then the bubbling phase. (It is recommended that during the interview, you can simply draw this W3C event flow, it will be helpful to explain the concept!)

image
圖片來源:W3C

As can be seen from the figure above, the so-called capturing phase means that when an event is triggered, for example, the user clicks a button, the topmost Window of the DOM tree goes all the way down to pass the event down and execute it. Actually, in the code, you need to add {capturing: true} to the event listener to enable the capturing mechanism.

Event bubbling

The bubbling stage is more commonly used. Contrary to the capturing stage, it first executes the event handler on the target, then passes it to the parent layer, then passes it to the grandparent layer, and then passes it all the way up.

<form onclick="alert('form click event triggered')">
  This is a form element
  <div onclick="alert('div click event triggered')">
    This is a div element
    <p onclick="alert('p click event fires')">This is a p element</p>
  </div>
</form>

Using the above example (it is recommended to write this example simply and quickly during the interview, it can help explain the concept), when we assign an onclick handler in the child element <p>, and click on it, we will see alert. It is because the onclick of the parent <div> is also triggered, and the onclick of the grandparent <form> is also triggered.

Here is a detail that needs to be noted. this during bubbling is not necessarily equal to event.target, but equal to event.currentTarget. In other words, thisis the currently executing handler; and event.targetwill always be the one that was actually clicked (in this case the innermost<p>).

In practice, sometimes we don't want bubbling, for example, we only want the events of the child elements to be triggered, and we don't want the elements of the parent to be triggered, so as to avoid interference. To achieve so, you can add event.stopPropagation() to the handler to avoid event bubbling. Nevertheless, this will still allow the handler to execute, (it just will not bubble up). If you want to stop the bubbling and prevent handlers on the current element from running, you can use event.stopImmediatePropagation().

☕️ Support Us
Your support will help us to continue to provide quality content.👉 Buy Me a Coffee