Event Delegation in JavaScript

Adding a large number of event handlers to a page has an adverse affect on performance and a common technique for alleviating this problem is known as event delegation where we add a single event handler to a parent element somewhere up the heirarchy. This works due to the flow of events through the DOM, and I put the technique to good use in my Interactive Periodic Table. In this post I will demonstrate event delegation using a simplified version of the Periodic Table.

The screenshot below shows the simplified Periodic Table and the alert shown when a user clicks on one of the elements.

The whole onclick/alert thing is ostensibly very simple JavaScript but note that here we have 118 chemical elements, each waiting eagerly for someone to click on them. The table is populated by looping through the data, adding a <td> element for each. Within this loop we could add an onclick handler to each <td> but this would both increase the time taken to populate the table and take up a lot more memory.

How the code actually works is to add just one click event handler to the <table> element and allow the click events of the various <td> elements to bubble up the DOM, a far more efficient solution. Events actually bubble all the way up through the <html> element to the document itself so you can use event delegation at the level of the entire page if you need to.

The source code for this project is available as a ZIP or a Github repository, the links to which are below.

Source Code Links

ZIP File
GitHub

I won't list the entire code, just the parts relevant to event delegation which are in the periodictabledisplay.js file. This file contains a class called PeriodicTableDisplay and its constructor includes this line:

Adding a click event handler to the <table>

document.getElementById(this._tableid).addEventListener('click', event => this._elementClicked(event));

Now let's look at the _elementClicked function.

_elementClicked function

_elementClicked(event)
{
    let target = event.target;

    if(target.parentElement.classList.contains("elementcell"))
    {
        target = event.target.parentElement;
    }

    if(target.classList.contains("elementcell"))
    {
        let element = this._periodictable.GetElement(target.dataset.atomicnumber);

        let details = `Name: ${element.name}\nAtomic Number: ${element.atomicnumber}\nChemical Symbol: ${element.symbol}`;

        alert(details);
    }
}

The event object passed to the event handler has a property called target, this being the element which was actually clicked, so we need to figure out whether the user clicked a chemical element or some other part of the table which can be ignored. We do this by checking the target for a CSS class called elementcell. We also have the added complication that each <td> contains a <span> to show the chemical symbol, either of which can be clicked. If the user clicked on an element whose parent has the CSS class elementcell then it is a span and target is reassigned to its parent.

That is the end of the code relevant to event delegation but just for the sake of completeness, if the user clicked on an actual chemical element we pick up its atomic number, assemble a string of details and show it as an alert. For this I have used data attributes, discussed here Using HTML5 Data Attributes with JavaScript

Conclusion

As you can see event delegation is straightforward to implement. If you find yourself adding a large number of handlers for a particular type of event just do this instead:

  • Add a single event handler to the parent element of the elements raising the events

  • In the handler function identify the actual element raising the event through the event's target property. You will probably need to look at its id, class or data- attributes and perhaps use an if/else or switch/case construct.

Looking for a VPN?

StrongVPN is running a promotion where customers can get a permanent discount on the monthly plan for just $5 for life! Ends June 30, 2020.