Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Replacing jQuery.live() with jQuery.on()


:P
On this page:

jQuery 1.9 and 1.10 have introduced a host of changes, but for the most part these changes are mostly transparent to existing application usage of jQuery. After spending some time last week with a few of my projects and going through them with a specific eye for jQuery failures I found that for the most part there wasn't a big issue. The vast majority of code continues to run just fine with either 1.9 or 1.10 (which are supposed to be in sync but with 1.10 removing support for legacy Internet Explorer pre-9.0 versions).

However, one particular change in the new versions has caused me quite a bit of update trouble, is the removal of the jQuery.live() function. This is my own fault I suppose - .live() has been deprecated for a while, but with 1.9 and later it was finally removed altogether from jQuery. In the past I had quite a bit of jQuery code that used .live() and it's one of the things that's holding back my upgrade process, although I'm slowly cleaning up my code and switching to the .on() function as the replacement.

jQuery.live()

jQuery.live() was introduced a long time ago to simplify handling events on matched elements that exist currently on the document and those that are are added in the future and also match the selector. jQuery uses event bubbling, special event binding, plus some magic using meta data attached to a parent level element to check and see if the original target event element matches the selected selected elements (for more info see Elijah Manor's comment below).

An Example

Assume a list of items like the following in HTML for example and further assume that the items in this list can be appended to at a later point. In this app there's a smallish initial list that loads to start, and as the user scrolls towards the end of the initial small list more items are loaded dynamically and added to the list.

<div id="PostItemContainer" class="scrollbox"> <div class="postitem" data-id="4z6qhomm"> <div class="post-icon"></div> <div class="postitemheader"><a href="show/4z6qhomm" target="Content">1999 Buick Century For Sale!</a></div> <div class="postitemprice rightalign">$ 3,500 O.B.O.</div> <div class="smalltext leftalign">Jun. 07 @ 1:06am</div> <div class="post-byline">- Vehicles - Automobiles</div> </div> <div class="postitem" data-id="2jtvuu17"> <div class="postitemheader"><a href="show/2jtvuu17" target="Content">Toyota VAN 1987</a></div> <div class="postitemprice rightalign">$950</div> <div class="smalltext leftalign">Jun. 07 @ 12:29am</div> <div class="post-byline">- Vehicles - Automobiles</div> </div>

… </div>

With the jQuery.live() function you could easily select elements and hook up a click handler like this:

$(".postitem").live("click", function() {...});

Simple and perfectly readable. The behavior of the .live handler generally was the same as the corresponding simple event handlers like .click(), except that you have to explicitly name the event instead of using one of the methods.

Re-writing with jQuery.on()

With .live() removed in 1.9 and later we have to re-write .live() code above with an alternative.

The jQuery documentation points you at the .on() or .delegate() functions to update your code. jQuery.on() is a more generic event handler function, and it's what jQuery uses internally to map the high level event functions like .click(),.change() etc. that jQuery exposes.

Using jQuery.on() however is not a one to one replacement of the .live() function. While .on() can handle events directly and use the same syntax as .live() did, you'll find if you simply switch out .live() with .on() that events on not-yet existing elements will not fire. IOW, the key feature of .live() is not working.

You can use .on() to get the desired effect however, but you have to change the syntax to explicitly handle the event you're interested in on the container and then provide a filter selector to specify which elements you are actually interested in for handling the event for.

Sounds more complicated than it is and it's easier to see with an example. For the list above hooking .postitem clicks, using jQuery.on() looks like this:

$("#PostItemContainer").on("click", ".postitem", function() {...});

You specify a container that can handle the .click event and then provide a filter selector to find the child elements that trigger the  the actual event. So here #PostItemContainer contains many .postitems, whose click events I want to handle. Any container will do including document, but I tend to use the container closest to the elements I actually want to handle the events on to minimize the event bubbling that occurs to capture the event.

With this code I get the same behavior as with .live() and now as new .postitem elements are added the click events are always available. Sweet.

Here's the full event signature for the .on() function:

.on( events [, selector ] [, data ], handler(eventObject) )

Note that the selector is optional - if you omit it you essentially create a simple event handler that handles the event directly on the selected object. The filter/child selector required if you want life-like - uh, .live() like behavior to happen.

While it's a bit more verbose than what .live() did, .on() provides the same functionality by being more explicit on what your parent container for trapping events is.

.on() is good Practice even for ordinary static Element Lists

As a side note, it's a good practice to use jQuery.on() or jQuery.delegate() for events in most cases anyway, using this 'container event trapping' syntax. That's because rather than requiring lots of event handlers on each of the child elements (.postitem in the sample above), there's just one event handler on the container, and only when clicked does jQuery drill down to find the matching filter element and tries to match it to the originating element. In the early days of jQuery I used manually build handlers that did this and manually drilled from the event object into the originalTarget to determine if it's a matching element. With later versions of jQuery the various event functions in jQuery essentially provide this functionality out of the box with functions like .on() and .delegate().

All of this is nothing new, but I thought I'd write this up because I have on a few occasions forgotten what exactly was needed to replace the many .live() function calls that litter my code - especially older code. This will be a nice reminder next time I have a memory blank on this topic. And maybe along the way I've helped one or two of you as well to clean up your .live() code…

Posted in jQuery  

The Voices of Reason


 

rafe
June 12, 2013

# re: Replacing jQuery.live() with jQuery.on()

Great post!

"One downside of .on() vs. .live in the not-yet-rendered scenario is that in order for it to work you have to know what the parent element is going to be."

I generally use (no performance problems):

$(document).on("click", ".postitem", function() {...});

Rick Strahl
June 12, 2013

# re: Replacing jQuery.live() with jQuery.on()

@rafe - Yup, for click events that works because it's universal. But if you're dealing with something else like a change or custom event that wouldn't work since document doesn't have it. But for common UI events typically document should be fine.

Elijah Manor
June 12, 2013

# re: Replacing jQuery.live() with jQuery.on()

Rick,

Glad to see you are converting your .live() methods over to the .on() method.

The reason .live() used to work is that it stored metadata about the selector, event type, and event handler onto the document object. Then when events were triggered in the DOM they would eventually bubble up to the document level where jQuery would look through the metadata seeing if there was a match.

The .on() equivalent to that would be using the 3 parameters version against the document.

$(".postitem").live("click", function() {...});


... would now be...

$(document).on("click", ".postitem", function() {...});


This techniques works fine with whatever event type you wish to use. All they are doing is storing metadata onto the document to be looked at later. You can actually poke around the data to look around, which is interesting.

You can also decide to store this metadata closer to the DOM elements you are interested, like you showed in your blog post.

So for $("#PostItemContainer").on("click", ".postitem", function() {...}); what is happening is that jQuery is putting all the metadata ("click" event type, ".postitem" selector, and the callback) onto the #PostItemContainer element. This can be handy since the events triggered now only have to bubble up to the #PostItemContainer element before jQuery looks through its metadata to see if there is a match.

What makes it even more confusing is that there is a 2 parameter version of .on() that works completely different than the 3 parameter version. The 2 param version acts the same as the event helper methods (click, hover, etc) and the bind method, where it attaches the event type and callback directly to the element, which means it has to exists at that point in time.

A while back I wrote an article comparing the various methods (click, live, bind, delegate, on).

http://www.elijahmanor.com/2012/02/differences-between-jquery-bind-vs-live.html

Harry McIntyre
June 13, 2013

# re: Replacing jQuery.live() with jQuery.on()

If you do need to upgrade jquery, but don't have time to update all the code using .live etc., you can install the jquery migrate package, which is a comparability shim.

http://blog.jquery.com/2013/05/08/jquery-migrate-1-2-1-released/

Liam Blizard
June 13, 2013

# re: Replacing jQuery.live() with jQuery.on()

"The vast majority of code continues to run just fine with either 1.9 or 1.10 (which are supposed to be in sync but with 1.10 removing support for legacy Internet Explorer pre-9.0 versions)."


It's jQuery 2.0 that removes support for IE6,7, & 8.

jQuery 1.10 is intended for those who need legacy IE support.

mvd
July 25, 2013

# re: Replacing jQuery.live() with jQuery.on()

Hi ! Moving on from .live to .on, i have this problem: needing the event trigger even after ajax or dom reconstruction, i started using $(document).on('click', '.thing', function(){blabla}) which works fine. But in a plugin, i don't always know the selector, and have to pass through the dom, for instance, a click event would be
$element.find('> .wrapper > .field > .add button').live('click', function(){blabla});

and i havent found any other way to do it with .on than
$element.on('click', '> .wrapper > .field > .add button', function(){blabla});

which does does not bind the event to the document, and hence does not perform as live, ie when a new element appears, the event is not bound.
Any idea how to use $(document).on() along with a way to traverse the DOM?
thanks !

To-mos
February 17, 2017

# re: Replacing jQuery.live() with jQuery.on()

Interesting read on the differences between bind, live, delegate, and on. As it turns out bind, live and delegate all pass their inputs to the on function anyway.

http://elijahmanor.com/differences-between-jquery-bind-vs-live-vs-delegate-vs-on/


West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024