The Case for jQuery’s .delegate() Method for Dynamic Event Binding

November 12th, 2010

Binding events is one of the most common tasks a JavaScript developer has to do. It’s integral to the way our applications handle user interaction and response. Since it’s inception, jQuery has had event handling via various helper methods that helped to abstract cross-browser differences. If you’re a jQuery developer, I’m sure you’re used to syntax like this:

$("#clname").click( function() { alert( "Rey" ) } );

which would bind a click event to a DOM element with an ID of “clname”. Very easy to understanding and simple to use. But when you get past just simple use of JavaScript and start inserting new DOM nodes, this binding becomes less useful because it can’t handle dynamically inserted elements. Let me explain.

Say you have the following HTML markup:

<div id="clcontainer">
<a href="#" class="clients">Click for Rey Information</a>

and you bind the click event to generate an alert of some type like this:

$(".clients").click( function() { alert( "Here's some information." ) } );

but then you decide that you’re going to add a new DOM element to the page dynamically:

$("#clcontainer" ).append( "<a href='#' class='clients'>Click for Mark Information</a>" )

What do you think will happen? I would venture many developers might expect that the second, dynamically appended DOM element would share the same binding as the first since they share the same class name. That’s not the case. jQuery methods such as click(), dbclick(), and bind() are meant to bind events to a specific set of available DOM elements (i.e: not dynamically appended). (Note: I updated this part to better clarify what I meant based on feedback by Jamie Newman)

Now to get around this limitation, jQuery team member Brandon Aaron initially created a plugin called LiveQuery which would allow you to bind events not only to specific DOM elements but all subsequent DOM elements that were appended to the DOM that matched the selector specified in the initial binding. That plugin eventually became part of the jQuery Core library and renamed simply to live(). The great thing with live() is that you could now bind dynamically added DOM elements like this:

$(".clients").live( "click", function() { alert( "Here's some information." ) } );

and if you did append a new DOM element, like this:

$("#clcontainer" ).append( "<a href='#' class='clients'>Click for Mark Information</a>" )

it would now share the same function binding as initially defined.

The Problem with Live()

Now, live() is an awesome method and people totally loved it. That is, until they wanted to bind events based on a deeper DOM traversal than just a single element, specifically when methods were used that alter the selector expression’s initial results (e.g.: using children()). So, if we had markup like this:

<div id="clcontainer">

and tried to use live() to bind all of the list items like this:

$("#clcontainer").children( "ul" ).find( "li" ).live( "click", function() { alert( "Here's some information." ) } );

the binding would fail. Since chaining is so widely used within the jQuery community, this was a bit of a surprise to many and one of the most requested updates to jQuery.

In order to get around this, Brandon introduced in jQuery v 1.4 the new delegate() method. It provides greater control by allowing you to specify the context to which you’d like to bind to. So using the same example as above and making some slight changes to use delegate(), we’re now able to use chaining to determine our selector results and then specify that we’d like all current and future list items to be bound to our declared function:

$("#clcontainer").children( "ul" ).delegate( "li", "click", function() { alert( "Here's some information." ) } );

When to Use live() or delegate()

These two convenience methods are totally awesome and incredibly helpful with more complex apps. The best use case for them is when you know that you will be dynamically adding new DOM elements and they’ll share the same bound function. I mean, essentially that’s the premise of event delegation. You’re trying to limit the number of event handlers needed to handle functionality and increase maintainability be centralizing your logic. Make sense. If you’re not going to be doing anything that involved, though, then jQuery’s event helper methods such as bind(), click() etc. are still excellent choices for those one-off scenarios.

Leave a Reply