How to do event delegation

JavaScript polyfill, by , Tuesday, June 24th, 2014

In this article I am going to give my angle on event delegation, show how to implement it using Vanilla JavaScript and also give my input on why it is important. Many libraries such as jQuery or YUI handles this elegantly, providing you with an easy to use interface, making sure that even you might not fully understand what it does for you, you can still harness the benefits. I am not going to show you how to use these libraries in this article, but merely give you the lower level explanation of how it works and how you could go about implementing it using pure JavaScript. This I believe should be more educational, on which I hope you agree.

What is event delegation?

If you have a set of elements that needs to respond to some event, instead of attaching an event to each element, you attach one event to a common parent of those elements. You then listen for any events bubbling up from its children, and fire what ever functions needed to be executed in responds to the event.

Why do event delegation?

The reasons for doing event delegation depends on what you are trying to achieve and the size of the project in which you are trying to achieve it. In any large or medium size web application, the number of events attached to individual elements of the DOM tree has an performance impact, and that has to be a concern of any developer involved. If you work on a website with hundreds or maybe thousands of DOM elements, having events executing the same functions spread over potentially a hundred elements can be wasteful and very hard to keep track of, and even worse, rendering your page slow to respond leaving your visitors with a bad user experience. By delegating events to common parents of related elements, makes not just for much faster code and easier maintainability but also a faster responding interface and hence a better user experience.

You might hear people say that the performance effect of good event delegation is minimal and not worth the effort, but that normally comes from those who have never worked on big complex projects, or maybe been so lucky working on some who already handles this very well, and therefore never experienced any problems. Having worked on very big complex e-commerce systems, I have experienced many times both the performance and maintenance benefits of doing proper event delegation, so trust me it is worth the effort!

Another reason for doing event delegation is in the case where you have dynamically added elements appearing on your website. If these elements needs a common event handler, it would be, not just cumbersome, but also wasteful to attach and remove these each time any of the given elements step in or out of the DOM tree. Instead having the event listener attached to a shared parent element, makes for much fast, much easier code to read and even better code to maintain.

Give me a practical example, please

Talking and explaining is all good and informative, but in the end it is all about doing, so lets look at a practical example. On your website, you might have a ul element containing an arbitrary number of li elements, some of which needs to respond to a click event. These elements might be added dynamically or present from the page loads, but in any given case, we want to only deal with the event listener once. But first lets look at our HTML elements;

  1. <ul id="iamparent">
  2. <li>Click me!</li>
  3. <li class="iamclickable">Click me!</li>
  4. <li class="iamclickable">Click me!</li>
  5. </ul>

To reach our goal of only having one event listener, we attach it to the parent ul element and then listen for any events bubbling from elements with the class iamclickable, and we are going to do it like this;

  1. (function() {
  2. "use strict";
  3. var classes,
  4. // You might as well chain the event listener
  5. // to your getElementById call, but for readability
  6. // in this example, I first store the element in a variable.
  7. iamparent = document.getElementById('iamparent');
  8.  
  9. iamparent.addEventListener('click', function(evt) {
  10. if (evt.target) {
  11. classes = evt.target.className.split(' ');
  12. if (classes.indexOf('iamclickable') >= 0) {
  13. console.log('You have clicked a clickable element');
  14. }
  15. }
  16. }, false);
  17. }());

With this structure, more li elements can be added dynamically without having to worry about the event listener, which makes your foundation more flexible.

Limitations and browser issues

In this implementation we assume that the browser running the code supports addEventListener and Array.prototype.indexOf, but if they do not, you can use a polyfill or write some additional checks to overcome these compatibility issues. I have written a polyfill for Array.prototype.indexOf which you might want to have a look at. Regarding addEventListener the issue is a little more complicated, and not something I will cover in this article. But basically what you need to do is, to write a custom addEvent function that uses addEventListener if available or else uses attachEvent, making the proper bindings and finally fixing the event object. (or more precisely the event.target property of the event object) If this makes sense to you, writing such a function should be feasible, but if not, I hope to get time to write an article about how to do just that. Of course you can also just use a library like jQuery and no longer having to worry about browser compatibility, but for educational purposes, implementing it yourself will surely improve your overall understanding.

I hope this article have been of help to you, and if you have any input or comments about it, feel free to address them in my Boxsheep community.