Let's Make a Framework: Event Delegation

2010-09-16 00:00:00 +0100 by Alex R. Young

Welcome to part 30 of Let's Make a Framework, the ongoing series about
building a JavaScript framework.

If you haven't been following along, these articles are tagged with
lmaf. The project we're creating is called Turing.

Last week I wrote about mixing event handlers into the DOM finder chainable API. This week
I'll cover event delegation.

Ryan Cannon commented on last week's post to ask where event delegation was. I was planning on leaving this out until
later, but Ryan reminded me how important event delegation is to modern
browser-based JavaScript.

Event Delegation

Event delegation is where an event is attached to a parent element (or the whole document), then selectors are used to determine if a handler
should run.

Consider the following markup:

  Page 1
  Page 2
  Page 3

An event handler could be attached to #navigation and
watch for clicks on the links. The advantage of this is new links can be
added (perhaps through Ajax) and the event handler will still work. It
also uses less events when compared to attaching events to each link.

This approach was a revelation 5 or 6 years ago, but it's now considered
best practice for a wide range of applications.


jQuery offers two methods for dealing with this:

$('#navigation a').live('click', function() {
  // Handler

$('#navigation').delegate('a', 'click', function() {
  // Handler

The live and
delegate methods are very similar and share the same code underneath.

The way this used to work in Prototype (and other Prototype-influenced
frameworks) was like this:

$('navigation').observe('click', function(event) {
  var element = event.findElement('a');
  if (element) {
    // Handler

As you can see, jQuery's API saves a little bit of boilerplate code.
Prototype 1.7 introduced on which can be used like this:

$('navigation').on('click', 'a', function(event, element) {
  // Handler

This is more like jQuery's delegate method.


jQuery relies on several things to handle delegation. The main method is
liveHandler, in

  1. jQuery.data is used to track event handler objects in
    the current context
  2. closest is used to find elements based on the event's
    target and currentTarget, and the selectors found in the previous step
  3. Each matching element is compared against the selectors to generate
    a list of matching elements
  4. This element list is then used to run the user-supplied event
  5. If the handler returns false or propagation has been stopped, false
    will be returned

The Prototype approach is simpler -- it loses some of the flexibility of
the jQuery approach -- but might be a better candidate to base our
framework code on. Let's target the original Prototype style (that used
findElement) and make it generic.

function findElement(event, expression) {
  var element = Event.element(event);
  if (!expression) return element;
  while (element) {
    if (Prototype.Selector.match(element, expression)) {
      return Element.extend(element);
    element = element.parentNode;

This code loops through each element, going up the DOM, until an element
is found that matches the selector. Turing already has some internal DOM
methods we can use to implement this feature.

Next Week

In next week's tutorial I'll explain how to implement
findElement and use it to create a simple event delegation