What is event delegation in JavaScript?

JavaScript Important

Event delegation

Event delegation is a technique that allows you to handle events efficiently by attaching a single event listener to a parent element instead of multiple listeners to individual child elements. This pattern is especially useful when dealing with dynamic content or large numbers of similar elements.

Capturing and bubbling allow us to implement one of the most powerful event handling patterns called event delegation.

The idea is that if we have a lot of elements handled in a similar way, then instead of assigning a handler to each of them – we put a single handler on their common ancestor.

In the handler we get event.target to see where the event actually happened and handle it.

The HTML is like this:

<table id="elements-table">
<tr>
<th colspan="3">
<em>Elements</em> Chart: Symbol, Atomic Number,
Properties
</th>
</tr>
<tr>
<td class="nw">
<strong>Hydrogen</strong> <br />H <br />1
<br />Lightest Gas
</td>
2 more like this...
</tr>
<tr>
<td class="w">
<strong>Carbon</strong> <br />C <br />6 <br />Life
Element
</td>
2 more like this...
</tr>
<tr>
<td class="sw">
<strong>Iron</strong> <br />Fe <br />26 <br />Magnetic
</td>
2 more like this...
</tr>
</table>

The table has 9 cells, but there could be 99 or 9999, doesn’t matter.

Our task is to highlight a cell <td> on click.

Instead of assign an onclick handler to each <td> (can be many) – we’ll setup the “catch-all” handler on <table> element.

It will use event.target to get the clicked element and highlight it.

let selectedTd;

table.onclick = function (event) {
let target = event.target; // Get the actual clicked element

// Early return if not clicking on a TD
if (target.tagName != 'TD') return;

highlight(target);
};

// Handles the highlighting logic
function highlight(td) {
// Remove previous highlight if it exists
if (selectedTd) {
selectedTd.classList.remove('highlight');
}

// Apply new highlight
selectedTd = td;
selectedTd.classList.add('highlight');
}

Such a code doesn’t care how many cells there are in the table. We can add/remove <td> dynamically at any time and the highlighting will still work.

Still, there’s a drawback.

The click may occur not on the <td>, but inside it.

In our case if we take a look inside the HTML, we can see nested tags inside <td>, like <strong>:

<td>
<strong>Northwest</strong>
...
</td>

Naturally, if a click happens on that <strong> then it becomes the value of event.target.

Improved code

table.onclick = function(event) {
let td = event.target.closest('td'); // (1)

if (!td) return; // (2)

if (!table.contains(td)) return; // (3)

highlight(td); // (4)
};

Explanations

  • The method elem.closest(selector) returns the nearest ancestor that matches the selector. In our case we look for <td> on the way up from the source element.
  • If event.target is not inside any <td>, then the call returns immediately, as there’s nothing to do.
  • In case of nested tables, event.target may be a <td>, but lying outside of the current table. So we check if that’s actually our table’s <td>.
  • And, if it’s so, then highlight it.

As the result, we have a fast, efficient highlighting code, that doesn’t care about the total number of <td> in the table.

Playground

Try it yourself! Click on different elements in the periodic table below. Notice how the event delegation allows clicks to work even when you click on the text or symbols inside each cell.

Benefits of Event Delegation

  • Better Performance: Instead of attaching multiple event listeners, you only need one listener on the parent element.
  • Dynamic Elements: Works automatically with dynamically added/removed elements.
  • Memory Efficient: Reduces the number of event listeners in memory.
  • Less Code: Requires less code to maintain and manage events.
00:00