rob cherny.com

About

My name is Rob Cherny and I'm a professional Web Developer with 16 years of experience creating Web sites and Web-based applications. This site is where I write about my work, and random other things...

Learn More

Loading...

Close Tab

Web Development

JavaScript Event Delegation and Event Handlers

So JavaScript events support has gotten a lot more attention lately, as the promise of Ajax and RIA gets more and more mainstream. Companies such as Yahoo! have been pushing the limits of what RIA means and experimenting with more efficient ways of running applications in Web browsers that sometimes rival desktop software. The Yahoo! mail application is a great example of this.

At The Ajax Experience back in October I saw Nate Koechley from Yahoo! present on some of the techniques being used at Yahoo! to increase the performance of JavaScript applications in the browser. Not surprisingly, some of these involve more robust event handling architectures. The main gist of better performing event architectures is the notion of "Event Delegation" as opposed to traditional "Event Handling".

The very basic problem I've found is that 99% of the examples out there are built with the Yahoo! UI Library scripts, obscuring the actual JavaScript required. In this article I'll show the basic JavaScript required to enable event delegation.

Traditional JavaScript Event Handlers and Unobtrusive JavaScript

Traditional event handlers, while getting us where we are today, are inefficient.

If you consider any Ajax or RIA powered application, it's going to have potentially dozens of user interaction points depending on the nature of the app. Now, all those interaction points require event handlers to be tied together in order for user actions to be triggered.

If you're building JavaScript interactions right these days, you're using something called unobtrusive scripting techniques, which basically means wiring up your events and scripts without any code inline in your (X)HTML. You're going to use your access to the DOM and simply attach scripts to the objects on the page using a proper separation of content, presentation, now through JavaScript, behavior. You are not going to have inline onclick event handlers anywhere, period.

Hooking up all those handlers comes with a price, not only in the number of steps to hook them up, but in the cost associated with storing redundant pieces of code in the browsers memory. Not only that, but if your DOM changes, you've got to rewire your events because a new additions won't be aware of code run onload.

Consider the following:

  1. <ul id="listing">
  2.         <li><a href="#">Handlers Test</a></li>
  3.         <li><a href="#">Handlers Test</a></li>
  4.         <li><a href="#">Handlers Test</a></li>
  5.         <li><a href="#">Handlers Test</a></li>
  6. </ul>
  1. window.onload = function(){
  2.    var x = document.getElementById("listing");
  3.        x = x.getElementsByTagName("a");
  4.    for (var i = 0; i < x.length; i++){
  5.      (function(){
  6.         var z = i;
  7.         x[i].onclick = function(){
  8.            alert("clicking" + z);
  9.            return false;
  10.         };
  11.       })();
  12.    }
  13. }

The code executes, finds all anchors in this list, then invents and assigns an anonymous (nameless) function to each link in the list. It's perfectly unobtrusive. However, we've consumed that much space in memory dedicated to those click handlers. Imagine if there were even more events and objects involved.

Combine that with potentially dozens of other interaction points and, as Nate Koechley put it best, you have a scenario similar to the following:

  • object+event, object+event, object+event +
  • object+event, object+event, object+event +
  • object+event, object+event, object+event +
  • object+event, object+event, object+event +
  • = a whole lotta stuff...

Which adds up to a lot of objects, and a lot of events. Enter, event delegation.

Event Delegation and Event Bubbling

The basic idea is events are assigned to a smaller subset of the document, rather than each individual element, and then you can determine what was clicked because JavaScript exposes this through event bubbling. Event bubbling is the notion that a (for instance) clicked element registers a click, then the event "echoes" up through the nodes in the DOM to the top level -- however, you can grab that event and determine the originating source or "target" object on the page.

Several Yahoo! folks have done a fairly good deal of work in getting the word out on this technique, and demonstrated it's usefulness several times using the most excellent Yahoo UI Library. They also have a demo page here.

In particular, Christian Heilmann has posted several examples using event delegation and the Yahoo! UI Library.

Great examples. And I'm going to totally rip him off, and without, uh, permission. I've copied and reworked the first example which shows two pairs of unordered lists with expand-collapse features built in, the first wired with traditional event handlers, the second with event delegation. The example demonstrates both methods working, until you modify the DOM, where only the second, event delegation example, works.

You need a function which gets the event's target (as defined in the W3C's event model) or, it's event source (as defined by our friend Internet Explorer). So, the magic is really the following function:

  1. // get and identify the source of the event object
  2. function getTarget(x){
  3.     x = x || window.event;
  4.     return x.target || x.srcElement;
  5. }

You get the target source node of the event, called as follows:

  1. two.onclick = function(e){
  2.   // delegate, pass in the event object ! !
  3.   var t = getTarget(e);
  4.   // take conditional action ! !
  5.   if (t.nodeName.toLowerCase() === 'a') {
  6. ..... 

Then, you take some conditional action based on what registered that call. My copy of his full demo is here, and the script is here.

His example shows how to attach something at a specific node, such as the top of an unordered list. However, another way to do this is literally to attach the code to the root of the document and then do something based on what triggered the event call.

I have a full example of this here (script). My example outputs the debugging data calls to the excellent Firebug console.log, so if you have it installed, press F12 to show the console. If you don't have Firebug (which is just outright tragic), I've included the Firebug Lite scripts which opens a similar console at the bottom of the page.

In this simple example, you can see I've tied the onclick handler to the document object and it can identify exactly what was clicked on at the top level.

  1. document.onclick = function(e) {
  2.   // get either event (W3C) or
  3.   // window event object (MSIE)
  4.   e = e || window.event;
  5.   // get either target (W3C) or event source (MSIE)
  6.   var t = e.target || e.srcElement;
  7.        
  8.   // for Firebug console
  9.   console.log(t.nodeName + ": this or parent id #" +
  10.     (t.nodeName == "A" ? t.parentNode.parentNode.id :
  11.        t.id || t.parentNode.id));
  12.   return false;
  13. }

It's really pretty easy.

Conclusions about Event Delegation

In the end, event delegation is:

  • Is easier to assign
  • Can consolidate all events into a nicer centralized package (think of it as a traffic cop) which distributes functionality from one set of events
  • Persists after the DOM has loaded, and if it is modified
  • Uses less memory footprint in your browser window
  • Consequently, may perform better
  • On a large scale desktop-like app, it will perform better

Feb 12, 02:29 PM in Web Development (filed under JavaScript)

  1. Robert    Feb 9, 02:00 AM    #

    Doug Crockford briefly spoke about this in his DOM theory talk. Link (I’d post an actual link, but your Textile help link seems to produce unhelpful results in Safar… feel free to edit this reply to make it work). I haven’t made up my mind about delegation. While in some instances it is absolutely necessary, I don’t know if it is practical for most of the pages I’ve worked with. I like the idea, though.

  2. rob    Feb 9, 01:12 PM    #

    Hey Robert, Doug Crockford’s talks are great.

    Yeah, for small scale operations or pages which aren’t going to have significant modification, I think it’s not necessarily the right way to go.

    For larger JavaScript apps or RIA type scenarios, then I think we should save what we can.

    Hmmmm, Textpattern may have an issue with Safari and the default link for the help form. One thing on my “todo” list is to change the setup of the comments on this site, sorry about that. I fixed the link, and thanks for the heads up. I don’t have a Mac at home (yet, there may be a change coming) to test on. Textile isn’t the greatest for the average visitor necessarily …

  3. Nate Koechley    Feb 11, 12:33 AM    #

    Hey Rob,

    Nice write up. I’m obviously biased toward our YUI library, but I think you’ve done a service by explaining it this way. Seeing things from a different angle always enriches one’s perspective, so thanks, I think you’ve helped a lot of people with this one.

commenting closed for this article

In This Section