Event Delegation with jQuery

Tweet

jQuery makes event handling in JavaScript easy. However, the more event handlers you define, the more memory you use, which can end up decreasing performance and making the UI sluggish. This article looks at how event delegation can help prevent this, and how you can apply event delegation with jQuery.

Event delegation is an event handling technique where, instead of attaching event handlers directly to every element you want to listen to events on, you attach a single event handler to a parent element of those elements to listen for events occurring on it’s descendant elements. When handling the event, you check which element fired the event, and respond accordingly. Event delegation relies on event bubbling in the DOM. This is the process whereby an event triggered on a child element propagates up the DOM tree to its parent element, and its parent’s parent element, etc., until the document is reached. Bubbling can also be stopped by the programmer using event.stopPropagation(). Note that not all DOM events propagate – focus, blur, load, and unload don’t.

Event delegation uses less memory because you replace multiple event handlers with a single event handler. For example, if you attach event handlers to each link in a list of ten links, you’d have ten event handlers taking up space in memory. If, instead, you use event delegation and handle events at the parent <ul> element, you only create one event handler and use less memory than you would’ve attaching to each link individually. In addition to reduced memory consumption, event delegation also has the following benefits.

  • No need to manually manage events when elements are added or removed from the DOM. If we used traditional event handling, we’d have to attach event handlers to elements added to the DOM, and remove event handlers from elements removed from the DOM.
  • Less code to manage, through fewer event handling functions. This can leave us with simpler code, without any duplicated event handling logic, which can help keep our JavaScript nice and DRY.

An Example of Event Delegation in jQuery

Suppose you’re developing a single page application that sells pictures of kittens. When the page loads, the first 20 kittens are displayed. As the user scrolls down the page, more kittens are loaded. Our HTML is shown below.

<section id="cats">
  <ul>
    <li>
      <img src="http://placekitten.com/200/200" alt=""/>
      <a href="/moreinfo">More info</a>
      <button>Add to cart</button>
    </li>
    ...
  </ul>
</section>

With traditional event handling, we’ll need to wire up event handlers to:

  1. Display a larger picture when the user clicks on a thumbnail.
  2. Display more info when the user clicks on the ‘More info’ link.
  3. Add the picture to the shopping cart when the user clicks ‘Add to cart’.
  4. Attach these three events to the new DOM elements that are added as the user scrolls down the page.

This code will resemble the following example. Note that this is boilerplate code intended to show how attaching event handlers to individual elements differs from using event delegation, so no implementation is given for the loadImage(), moreInfo(), addToCart(), and loadNewKittens() functions.

$(document).ready(function() {
  var cats = $('#cats');

  cats.find('img')
    .on('click', function() {
      loadImage();
    })

  cats.find('a')
    .on('click', function(event) {
      event.preventDefault();
      moreInfo();
    });

  cats.find('button')
    .on('click', function() {
      addToCart();
    });

  $(window).scroll(function() {
    var fragment = loadNewKittens();
    // attach event handlers for new DOM elements
    fragment.find('img')
      .on('click', function() {
        loadImage();
      });

    fragment.find('a')
      .on('click', function(event) {
        event.preventDefault();
        moreInfo();
      });

    fragment.find('button')
      .on('click', function() {
        addToCart();
      });

    fragment.appendTo('#cats ul');
  });
});

That’s quite a bit of code. Now let’s see how our code looks if instead we use event delegation:

$(document).ready(function() {
  $('#cats')
    .on('click', 'img, a, button', function(event) {
      event.preventDefault();
      var target = event.target;

  switch(target.tagName.toLowerCase()) {
    case 'img':
      loadImage();
      break;
    case 'a':
      moreInfo();
      break;
    case 'button':
      addToCart();
      break;
    default:
      // do nothing
  }
});

  $(window).scroll(function() {
    var fragment = loadNewKittens();
    fragment.appendTo('#cats ul');
  });
});

The key is the optional second argument to on(). By passing a selector here, on() knows it’s dealing with a delegated event handler rather than a directly bound event handler.

Our event handling code is a lot simpler now too. By getting a hold of event.target, and switching on it’s tagName, we can tell which element fired the event and can respond appropriately. Plus, we no longer have to attach event handlers for elements loaded in $(window).scroll, as the events fired by these new elements are delegated to the parent element.

A potential ‘gotcha’ to be aware of when using event delegation is that any event handlers attached to child elements are handled before the deletated event handler fires. Therefore, it’s possible for a child event handler to call event.stopPropagation() or return false, which will prevent the event from bubbling up to the delegated event handler, and leave you scratching your head as to why your event isn’t being delegated.

Conclusion

In this article we’ve looked at event delegation. We’ve seen how it can help improve the performance of your site by lessening the event handling load it has to bear. We’ve also seen how to implement event delegation in jQuery via the on() function.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Tim

    This is extremly useful, I was just up to refactor my GUI related jQuery Code and you spared me a lot of thinking and research with this article. thx.

    And before I forget: Kittens, FTW!

  • MS

    Thanks for your explanations on how to use the jquery .on() method! Especially the ‘gotcha’ section was helpful ;-)

  • http://www.brothercake.com/ James Edwards

    Strictly speaking – focus, blur, load, and unload are not DOM events, and the first two do actually propagate in Firefox (though they’re not supposed to). The DOM equivalents of focus and blur are DOMFocusIn and DOMFocusOut, which do propagate.

    There are also some tedious exceptions here and there, for example onchange events don’t bubble in IE8 or earlier. You just have to keep an eye out for cases like that, and be prepared to fallback on individual events.

  • Benedict Sefa

    Great article! I was wondering whether you could go into how jQuery makes it possible to build an event driven interface that appears asynchronous given that JavaScript is single threaded.