Event Delegation with jQuery

Thursday, March 4, 2010

jQuery makes implementing event delegation quick and easy! In version 1.4.2 there are now three ways to utilize event delegation: .live(), .delegate(), and do-it-yourself. Each technique progressively gives you a little more control and flexibility. Lets take a look at these three techniques with a quick 101 crash course on event delegation first.

Event Delegation 101

At its core event delegation is all about letting events bubble up the DOM tree to a parent element. This provides several advantages such as only binding one event handler instead of potentially 100s and it works with elements currently in the DOM at runtime and those which are injected after runtime.

Imagine you have a large table of data and you want to do something when the user clicks on a row. You might first start out by binding a click event to each <tr> which would look like this.

$('tr').bind('click', function(event) {
    // this == tr element
});

If you have lots and lots of table rows it could take a while to bind all those events. Not to mention the browser now needs to keep track of all those event handlers. Instead, we can use event delegation by binding the click event to the table (or any parent element, maybe even the body) and letting the event bubble up to the table. Then we can inspect the event to see which element was actually clicked on. jQuery either does this for you or makes this part easy and we’ll look at how to do this in just a moment.

The second reason you might want to use event delegation is for automatically handling dynamic data. Lets say you needed to dynamically add rows to your table. Well, then you’d have to also bind the click event to those new rows. With event delegation the event is actually bound to the table element and any new rows do not need a new event bound. Awesome!

.live()

The .live() method, added in jQuery 1.3, provides the most simple way to implement event delegation and is suitable for simple scenarios. Here is some example code illustrating a click event being captured for all <tr> elements (old or newly created) on a page.

$('tr').live('click', function(event) {
    // this == tr element
});

You can see this example code in action here.

Notice, I said “page”. The .live() method binds events to the document by default. You can actually change this, in jQuery 1.4, by passing in a new context to jQuery. If you are unfamiliar with the context in jQuery, I recommend reading this blog post: Understanding the Context in jQuery.

One of the gotchas of this method is that it isn’t exactly chainable like other jQuery methods. For example if you use a jQuery method that changes the selection of elements, such as .children() or .parent(), before calling .live() then it will not work. In other words, it works best when only used with the given selector. The following code example doesn’t work.

$('#myContainer').children('table').find('tr').live('click', function(event) {
    /*** does not work! ***/
});

You might expect that the previous example would still handle click events for <tr> elements. However, it isn’t going to work at all because of the .children() method. So, .live() is a simple method for simple scenarios.

To stop .live() events, you’d use the .die() method.

.delegate()

The .delegate() method was introduced in jQuery 1.4.2 and provides a more focused way of doing your event delegation. Keeping with the table example lets look how we can do it with the .delegate() method.

$('table').delegate('tr', 'click', function(event) {
    // this == tr element
});

You can see this example code in action here.

First off, unlike .live() events the event handler is actually bound to the selected element (“table” in this case). Then the click event is filtered to only fire when the <tr> was clicked on. This is a great method as it makes it easy to specify (and understand) which element you want to delegate from and which elements to filter the event on.

This method also clears up the confusion with regards to the chainability that the .live() method introduced. Here is the example code that didn’t work with .live() but does work with delegate.

$(#myContainer').children('table').delegate('tr', 'click', function(event) {
    // this == tr element
});

Again, the reason this works and .live() doesn’t is because .delegate() is actually binding the event to the selected parent and then filtering based on the selector passed to the .delegate() method.

To stop delegated events, you’d use the .undelegate() method.

Do-It-Yourself

The last technique is the ability to just do-it-yourself. This is for advanced use-cases where you want to have more flexibility than .delegate() or .live(). Using a method called .closest(), which was introduced in jQuery 1.3, we are going to look at the event.target (which element the event happened on) and see if it or any of its parents are the element we want to filter the event on. Keeping with the table example our code looks like this.

$('table').bind('click', function(event) {
    // this == table element
    var $tr = $(event.target).closest('tr');
});

You can see this example code in action here.

As you can see we are just using the good’ol .bind() method to bind our click event to the table element directly. Then when the table is clicked we check the event.target to see if it is the <tr> or if one of its parents is the <tr>. If neither of these cases are true then $tr is an empty jQuery collection.

Summary

Use .live() if you just need to add some quick event delegation or handle events on elements that don’t yet exist in the DOM. Beware though that you shouldn’t chain .live() and by default it binds event handlers to the document element.

Use .delegate() when you want a little more control and want to chain method calls.

Use the do-it-yourself technique when you want ultimate control and flexibility with your event delegation.

Which do you prefer and why?

Posted in jQuery with 11 comments

Recent Posts

Special Events: The Changes in 1.4.2

Thursday, February 25, 2010

jQuery’s event system has seen incredible feature growth over the years and with that growth came some pains. jQuery 1.4.2 included a much needed overhaul of the event system. This also brought some backwards incompatible changes to the two new special eve…

Posted in jQuery with 1 comment

Connecting the Dots with Web Workers

Thursday, February 18, 2010

A little while back I sat down to experiment with Web Workers and decided I would write a zero-sum game. I choose Connect 4 as the game I’d use to explore Web Workers with. It helped that I found an algorithm written by Keith Pomakis in C. First things fir…

Posted in HTML5 with 2 comments

Understanding the Context in jQuery

Wednesday, June 24, 2009

When selecting elements jQuery has an optional second argument called context. This context provides a means to limit the search within a specific node. This is great when you have a very large DOM tree and need to find, for example, all the <a> tags…

Posted in jQuery with 23 comments

Automating with Special Events

Wednesday, June 17, 2009

David Walsh had a great idea to coerce an element that is given a click event to have a pointer for its cursor. The pointer is a universal symbol for users to know that something is clickable. Using this technique it would be “automatic” that any element g…

Posted in jQuery with 2 comments

jQuery Edge: New Special Event Hooks

Thursday, June 4, 2009

These Special Event hooks were changed in 1.4.2. Check out this blog post to see the details about the change. In jQuery 1.3.3 1.4 there are two new special event hooks: add and remove. These two hooks, unlike setup and teardown, are called for each event…

Posted in jQuery, jQuery Edge with 7 comments

jQuery Edge: Simplified .hover()

Thursday, May 28, 2009

In jQuery 1.3.3 1.4 the .hover() method will optionally accept a single method instead of always requiring two methods. This allows you to contain your logic within one method instead of duplicating it among two. I don’t know about you but I’ve avoided usi…

Posted in jQuery, jQuery Edge with 7 comments

jQuery Edge: Better Support for other Windows and Documents

Thursday, May 14, 2009

jQuery in the past hasn’t always played well with other windows and documents than the one it was loaded in. However, jQuery is moving in the right direction to help ease the pain of cross window/frame development. There have been several commits recently…

Posted in jQuery, jQuery Edge with 5 comments

jQuery Edge: Bind with a Different “this”

Tuesday, May 12, 2009

Brand new to jQuery SVN is the oft-requested feature of providing a different value for the “this” object in an event callback. Previously jQuery would always send the element as the value of “this” in the callback. You can utilize this new feature by pass…

Posted in jQuery, jQuery Edge with 10 comments

jQuery Edge: Live Events now with Data

Friday, May 8, 2009

In jQuery 1.3.3 1.4 the .live() method will have the ability to pass data along just like you might do with .bind(). The .bind() (and now .live()) method takes an optional second argument called “data”. The data can then be accessed within the event handle…

Posted in jQuery, jQuery Edge with 5 comments

jQuery Edge: Versatile .index()

Thursday, May 7, 2009

Paul Irish and ajpiano (via Google Groups) saw an opportunity to make the .index() method work better for them. Currently the .index() method looks for a given element within the jQuery collection. However, they wanted a quick way to get the index of the c…

Posted in jQuery, jQuery Edge with 4 comments