222

I am using jQuery v.1.7.1 where the .live() method is apparently deprecated.

The problem I am having is that when dynamically loading html into an element using:

$('#parent').load("http://..."); 

If I try and add a click event afterwards it does not register the event using either of these methods:

$('#parent').click(function() ...); 

or

// according to documentation this should be used instead of .live()
$('#child').on('click', function() ...); 

What is the correct way to achieve this functionality? It only seems to work with .live() for me, but I shouldn't be using that method. Note that #child is a dynamically loaded element.

Thanks.

Sean Thoman
  • 7,429
  • 6
  • 56
  • 103
  • 25
    Why do you say *"supposedly deprecated"*? Don't you believe the docs? –  Jan 06 '12 at 01:44
  • 6
    It's not _supposedly_ deprecated: it _is_ deprecated. If you look at the [jQuery doco for `.live()`](http://api.jquery.com/live/) it tells you how to rewrite existing uses of `.live()` to use `.delegate()` or `.on()` (depending on whether you're on version 1.7+ or not). Note though that if you add a handler with `.click()` "afterwards" as you mention, i.e., after dynamically loading elements, it should work - the only problem is trying to assign with `.click()` _before_ dynamically loading elements. – nnnnnn Jan 06 '12 at 01:47
  • I changed the wording to 'apparently' since that's basically what I meant. Anyways, I understand now that obviously since the .load() event is asynchronous then the #child element could only reliably be recognized in the success handler, which makes sense. – Sean Thoman Jan 06 '12 at 01:51
  • Possible duplicate of [Event binding on dynamically created elements?](http://stackoverflow.com/questions/203198/event-binding-on-dynamically-created-elements) – showdev Aug 10 '16 at 21:16

7 Answers7

617

If you want the click handler to work for an element that gets loaded dynamically, then you set the event handler on a parent object (that does not get loaded dynamically) and give it a selector that matches your dynamic object like this:

$('#parent').on("click", "#child", function() {});

The event handler will be attached to the #parent object and anytime a click event bubbles up to it that originated on #child, it will fire your click handler. This is called delegated event handling (the event handling is delegated to a parent object).

It's done this way because you can attach the event to the #parent object even when the #child object does not exist yet, but when it later exists and gets clicked on, the click event will bubble up to the #parent object, it will see that it originated on #child and there is an event handler for a click on #child and fire your event.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • 24
    **Great** explanation! I've never before been able to wrap my head around `live()` vs. `on()` but tonight I decided yet again to try, and your explanation immediately revealed what I had been missing all along. Thanks! – MikeSchinkel May 19 '13 at 06:20
  • 6
    **A million ups**. I started using knockout before I realized this was possible in jQuery for dynamically created children, thank you so much. – Brock Hensley Aug 15 '13 at 02:35
  • 9
    You should consider writing a book or something: your little text has been more helpful to me than the full page in the jQuery docs about «_on()_». **Thanks a lot!** – Cholesterol Sep 23 '13 at 18:53
  • @jfriend00 do you happen to know how we can apply this same process to a hover, non-hover situation? is there a source that talks about this? – klewis Jan 29 '16 at 20:52
  • @blackhawk - See [this answer](http://stackoverflow.com/a/14938484/816620). You can register for the `mouseenter` and `mouseleave` events and test inside the handler which one was triggered. jQuery's pseudo "hover" event is not available with delegation. – jfriend00 Jan 29 '16 at 21:04
34

Try this:

$('#parent').on('click', '#child', function() {
    // Code
});

From the $.on() documentation:

Event handlers are bound only to the currently selected elements; they must exist on the page at the time your code makes the call to .on().

Your #child element doesn't exist when you call $.on() on it, so the event isn't bound (unlike $.live()). #parent, however, does exist, so binding the event to that is fine.

The second argument in my code above acts as a 'filter' to only trigger if the event bubbled up to #parent from #child.

Bojangles
  • 99,427
  • 50
  • 170
  • 208
  • If I call the $.on() method after the $.load() method, then why wouldn't the #child element exist at that point? – Sean Thoman Jan 06 '12 at 01:41
  • 6
    @SeanThoman - you'd have to call it from the success handler of the `.load()` method - not from the code after the `.load()` method. `#child` is only known to be loaded when the success handler actually executes and not before. – jfriend00 Jan 06 '12 at 01:47
  • and even then the time it takes to insert whatever data you got back into the DOM means it might not connect. I've been doing a lot of single page app work lately and my standard is var $body = $('body'); $body.on("event", ".element/#class", function(e){}); for everything. 1 selector. No worry about what is or isn't loaded. – lupos Sep 25 '13 at 16:08
23

$(document).on('click', '.selector', function() { /* do stuff */ });

EDIT: I'm providing a bit more information on how this works, because... words. With this example, you are placing a listener on the entire document.

When you click on any element(s) matching .selector, the event bubbles up to the main document -- so long as there's no other listeners that call event.stopPropagation() method -- which would top the bubbling of an event to parent elements.

Instead of binding to a specific element or set of elements, you are listening for any events coming from elements that match the specified selector. This means you can create one listener, one time, that will automatically match currently existing elements as well as any dynamically added elements.

This is smart for a few reasons, including performance and memory utilization (in large scale applications)

EDIT:

Obviously, the closest parent element you can listen on is better, and you can use any element in place of document as long as the children you want to monitor events for are within that parent element... but that really does not have anything to do with the question.

Larry Williamson
  • 1,149
  • 5
  • 18
  • 23
    He tried `$(element).on(...)`. This is `$(document).on(...,element,...)`. Different beasts. – cHao Jan 06 '12 at 01:39
  • @ArnoldRoa yes, better performance. you won't have a listener on multiple children, do not have to reapply the listener any time the DOM is modified, and events bubble up to through the DOM by default. Run once and every child matching the selector will trigger the given function when clicked. – Larry Williamson May 10 '18 at 13:57
  • @L422Y's comment here is very misleading. If you're curious about performance implications, take a look at [@jfriend00's comment](https://stackoverflow.com/questions/8752321#comment10905215_8752377) on [@Jared's answer](https://stackoverflow.com/a/8752377) – Gust van de Wal Oct 01 '19 at 16:47
  • @GustvandeWal How is it misleading? If you go and issue a hundred $(this).on(...) vs listening for an event one time on a parent element, it is substantially more performant. – Larry Williamson Oct 02 '19 at 02:13
  • You're only talking about attaching the events. Real-world performance bottlenecks are too many listeners firing off (like ones that you delegate), not large amounts of elements that need an event attached to them. – Gust van de Wal Oct 02 '19 at 02:17
  • I think you're going way out on a limb here to argue about something that wasn't even part of the initial question – I've clarified that listening to a closer parent element is best practice, however, in MANY cases you would be incorrect, especially if anonymous functions are being used. – Larry Williamson Oct 02 '19 at 02:19
  • The thing is that Arnold asked about performance. Your follow-up comment didn't address much of the performance implications but rather its ease of use. That's why it's misleading. You're probably right about the bottleneck, though. I've once delegated multiple mousemove events and still have nightmares – Gust van de Wal Oct 02 '19 at 02:39
  • What is also a nightmare is REMOVING bindings to specific elements, it's just so much cleaner this way. Any other way can and almost always will (depending on the coder) end up in lots of memory leaks. When you are dealing with an interface that has a lot of rows or items, things can get out of hand very, very quickly! – Larry Williamson Oct 02 '19 at 15:27
14

The equivalent of .live() in 1.7 looks like this:

$(document).on('click', '#child', function() ...); 

Basically, watch the document for click events and filter them for #child.

Jared
  • 2,408
  • 2
  • 19
  • 33
  • Because that's how the event handler attaches to the document (the way `.live()` does). – cHao Jan 06 '12 at 01:40
  • 17
    One reason that `.live()` is deprecated now is because it's bad to put all live event handlers on the document object. Things can really, really slow down. Not only do events have to bubble all the way up to the document, but you may have a lot of event handlers to look through on the document object. THE main advantage of `.on()` is that you can attach it to a parent object that is much closer to the actual object and drastically improve performance. So ... I would NOT recommend using `.on()` with the document object. Much better to pick a closer parent object. – jfriend00 Jan 06 '12 at 01:49
  • Thanks for the clarification. For what it's worth the performance of events has been much improved with on() so it should be less of an issue than it used to be. If attaching to a parent, I would assume it would have to be a parent that exists on document ready or you'd see similar problems. – Jared Jan 06 '12 at 02:47
  • At worst, this *does* emulate the deprecated `.live()` approach; however the ability to control delegation is certainly advantageous. – Dan Lugg Mar 10 '13 at 00:56
10

I know it's a little late for an answer, but I've created a polyfill for the .live() method. I've tested it in jQuery 1.11, and it seems to work pretty well. I know that we're supposed to implement the .on() method wherever possible, but in big projects, where it's not possible to convert all .live() calls to the equivalent .on() calls for whatever reason, the following might work:

if(jQuery && !jQuery.fn.live) {
    jQuery.fn.live = function(evt, func) {
        $('body').on(evt, this.selector, func);
    }
}

Just include it after you load jQuery and before you call live().

NSV
  • 518
  • 7
  • 13
  • Replace the inner call with `return jQuery('body').on(evt, this.selector, func);`, otherwise method chaining won't work, and it won't work in environments where `$` is not available. – Skynet Jul 02 '22 at 13:12
6

.on() is for jQuery version 1.7 and above. If you have an older version, use this:

$("#SomeId").live("click",function(){
    //do stuff;
});
Matt Cashatt
  • 23,490
  • 28
  • 78
  • 111
  • 8
    OP says "I am using jQuery v.1.7.1 " – Selvakumar Arumugam Jan 06 '12 at 01:31
  • Alls I can really tell you is that it is not supported below that. I had bad luck with on() my self and reverted to live() on an enterprise app running jQuery 1.7.1. – Matt Cashatt Jan 06 '12 at 01:33
  • `.live()` has been deprecated for all versions of jQuery. Before 1.7, it is recommended to use `.delegate()`, not `.live()`. – jfriend00 Jan 06 '12 at 01:38
  • 1
    @jfriend--why not post your own answer instead of downvoting mine? I use live() every single day with all versions younger than 1.6 and it works fine. I can see that you are good at reading documentation, but sometimes it is more help to put something into practice for a person. .live() works. Period. – Matt Cashatt Jan 06 '12 at 01:41
  • 5
    @MatthewPatrickCashatt - I did post my own answer and did not downvote yours - don't go making such blind assumptions. Pre 1.7, `.delegate()` performs much better than `.live()` which is why the jQuery doc recommends it and deprecates `.live()`. Yes, `.live()` still works, but answers here on SO should recommend the better way of doing things. I've seen this 20 times here on SO. If you post code with `.live()` here on SO, it will get downvoted (not usually by me unless there's something else seriously wrong with it). – jfriend00 Jan 06 '12 at 01:54
  • 1
    I didn't downvote, but although `.live()` definitely works even in version 1.7 you should still use `.delegate()` or `.on()` because `.live()` was deprecated for a good reason. As long as you note the change in syntax for the two newer functions either will work fine. – nnnnnn Jan 06 '12 at 01:57
  • 1
    @jfriend00- You are absolutely right. Long day. My apologies. – Matt Cashatt Jan 06 '12 at 01:59
  • .live is a savior of older jQuery versions.Thanks dude. – sajanyamaha Jul 05 '15 at 11:37
4

I used 'live' in my project but one of my friend suggested that i should use 'on' instead of live. And when i tried to use that i experienced a problem like you had.

On my pages i create buttons table rows and many dom stuff dynamically. but when i use on the magic disappeared.

The other solutions like use it like a child just calls your functions every time on every click. But i find a way to make it happen again and here is the solution.

Write your code as:

function caller(){
    $('.ObjectYouWntToCall').on("click", function() {...magic...});
}

Call caller(); after you create your object in the page like this.

$('<dom class="ObjectYouWntToCall">bla... bla...<dom>').appendTo("#whereeveryouwant");
caller();

By this way your function is called when it is supposed to not every click on the page.

Coderx07
  • 198
  • 6