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

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:

[code lang=”js”]
$("#clname").click( function() { alert( "Rey" ) } );
[/code]

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:

[code lang=”html”]
<div id="clcontainer">
<a href="#" class="clients">Click for Rey Information</a>
</div>
[/code]

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

[code lang=”js”]
$(".clients").click( function() { alert( "Here’s some information." ) } );
[/code]

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

[code lang=”js”]
$("#clcontainer" ).append( "<a href=’#’ class=’clients’>Click for Mark Information</a>" )
[/code]

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:

[code lang=”js”]
$(".clients").live( "click", function() { alert( "Here’s some information." ) } );
[/code]

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

[code lang=”js”]
$("#clcontainer" ).append( "<a href=’#’ class=’clients’>Click for Mark Information</a>" )
[/code]

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:

[code lang=”html”]
<div id="clcontainer">
<ul>
<li>Mary</li>
<li>Jane</li>
</ul>
</div>
[/code]

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

[code lang=”js”]
$("#clcontainer").children( "ul" ).find( "li" ).live( "click", function() { alert( "Here’s some information." ) } );
[/code]

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:

[code lang=”js”]
$("#clcontainer").children( "ul" ).delegate( "li", "click", function() { alert( "Here’s some information." ) } );
[/code]

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.

Rey Bango

13 Comments

    • I agree with you, Live should be depreciated. I don’t understand why Live came first. I see your tests, thank you.

  1. Now that $.fn.delegate is available, I’m not sure there’s a compelling reason to use $.fn.live at all, considering that it is only sort of chainable and has potential performance implications because you can’t target a specific container to handle the delegation like you can with $.fn.live. The $.fn.live method has a nice quick feel to it, for sure, but I think it’s worth taking the time to understand and use delegate instead.

    • Definitely agree with you. delegate() is the best way to do it for those that have migrated to jQuery v1.4.x. If you’re still on v1.3.x, live() is the better choice.

    • Great article, thanks for the breakdown. Now that we have delegate() I agree with you both that there isn’t a very compelling reason to use live() any longer.

      I don’t agree with Jeff though that live() should be deprecated. However, I do think developers should be discouraged from using it. Maybe down the road it could be deprecated but I think it’s a bit early for that myself.

  2. i think sometimes its just better to go back to the basics
    and if you want a to add a new dom element with a click event
    and call a function that other elements call

    you can just add the element with the onclick attribute
    $(“#clcontainer” ).append( “<a href=’#’ rel=”nofollow”>Click for Mark Information</a>” )

    but avcorse for other more complex events like onchange you should better use live.

    what do you think?

  3. a) Could the live example work if you added a specific class to the li elements and attached to something like this instead?

    $(“li.foo”).live(…)

    b) while it demonstrates chainability, could the final example be written more concisely as this?

    $(“#clcontainer”).delegate( “li”, “click”, function() { alert( “Here’s some information.” ) } );

  4. Pingback: DotNetShoutout
  5. Why would you use $(“#clcontainer”).children( “ul” ).find( “li” ) instead of $(‘#clcontainer ul li’) ?

  6. Hi,
    I rather have a question instead of a comment. The question is that how can we determine that which element was clicked? For example, we are adding 8/10 hyperlinks “a” dynamically by using live() or delegate() to a parent “div”. Now, how would we determine that which of “a”s has been clicked and show different behaviour depending on that?

    Note: Consider initial ” as

    Thanks!!
    /Syed

  7. Rey,

    just listened to you on cfpanel and thanks for bringing this up. Did not realize, this may solve a problem with dynamically loaded content with deep jQuery requirements, live() fails requiring me to load jQuery with dynamic content creating a maintenance nightmare amongst other things.

    Kevin

Comments are closed.