One of the most used features of jQuery is DOM manipulation, but right after that are probably event binding and AJAX content loading. When we bind events to AJAX loaded or dynamically added elements our app might not behave as we would have expected. This is one of the oldest dilemmas of front-end development.
jQuery has addressed this problem back in version 1.3 by introducing .live()
method. Since then, the jQuery team has improved upon it and introduced .delegate()
and .on()
event binding methods. Depending on the jQuery version your project is using, you will need to use one of these methods.
In this post, I will try to help you identify the recommended way of binding events to AJAX loaded (or dynamically created) elements depending on jQuery version you are using. Also, cover some of the performance considerations you should be aware of.
Here is how methods usage groups. Choose the method that your jQuery version falls into.
Before we start, let's setup the page cotents and defind our business requirement. Assume we have the following HTML content. Clicking on the button adds a new list item. Here is a link to jsFiddle.
The requirement: we want to alert user when she clicks on any link in the list, including those created dynamically.
<ol>
<li>
<a href="#">Some list item</a>
</li>
</ol>
<button>Add another list item</button>
<script>
$(document).ready(function(){
$('button').click(function(){
$('ol').append('<li><a href="#">Dynamic list item</a></li>');
});
});
</script>
Now, that our page and requirements are set, let's see how to achieve our goals with different jQuery version.
1.7.0 < jQuery - Use .on() event binding method
If your project is using jQuery 1.7 and greater use recommended way of binding events - .on()
. It can bind direct and delegated events. For our case we must use delegated event binding. Delegated event binding means that you should bind event to the parent element and use selector as the second parameter.
// Here is the demo http://jsfiddle.net/aUTGH/1/
$('ol').on('click', 'a', function(){
alert( $(this).text() );
return false;
});
We are attaching one event to the parent/container and filtering out necessary elements with a selector ('a'). We must attach event handler to any parent element so that propagated events from newly created elements could also be caught. The following code would not fire alert window for newly created links.
// Will not be attached to dynamically created links
$('a').on('click', function(){
alert( $(this).text() );
return false;
});
This is also recommended way of attaching events. Since we are attaching only one event and it is attached right at the parent element, we are squeezing out every bit of performance.
For cases when you do not control or do not know where the content might be added, you can also attach your event handlers to document
object. This is fine for rarely occurring events such as click
. However, for frequent events such as mousemove
on large pages you might experience performance issues.
// Binding event to document
$(document).on('click', 'a', function(){
alert( $(this).text() );
return false;
});
Read official .on()
method documentation to learn about different method signatures.
1.4.2 < jQuery < 1.7 - Use .delegate() event binding method
The .delegate()
method is the predecessor of .on()
method and difference in method signature. All notes on performance and usage are the same as for the .on() method.
// Here is the demo http://jsfiddle.net/aUTGH/2/
$('ol').delegate('a', 'click', function(){
alert( $(this).text() );
return false;
});
// You can also attach event handler to document object
$(document).delegate('a', 'click', function(){
alert( $(this).text() );
return false;
});
Read official .delegate()
method documentation to learn about different method signatures.
1.3 < jQuery < 1.4.2 - Use .live() event binding method
The .live()
method is the first event binding method that addressed the problem. It was introduced in version 1.3, deprecated in 1.7 and completely removed in jQuery 1.9.
The .live()
method binds event handler to the document
and compares the selector to the event target. If they match it fires the event. It relies on event propagation and if any event handler on the page stops the propagation, events bound with .live()
method would not fire. Also, unbinding event from document object will remove all events bound by .live()
.
// Here is the demo http://jsfiddle.net/aUTGH/3/
$('li').live('click', function(){
alert( $(this).text() );
return false;
});
Read official .live()
method documentation for more details.
jQuery < 1.3 - Use .bind() event binding method
If you are still using jQuery version earlier than 1.3, please take your time and update. You owe it to yourself. Well, that out of the way, here are 2 methods to bind events to dynamically created elements in jQuery versions earlier than 1.3:
- Duplicate previous methods by attaching event to the parent element (or document.body). Use event propagation and
event.target
to check if the element is the one you are interested. - Explicitly bind your event to the newly loaded or dynamically created elements. For example, add
ajax-loaded
CSS class to all links that were loaded through AJAX and rebind click event to those links.
Method 1 - Use event propagation
$('ol').click(functino(event){
if( $(event.target).is('a') ){
alert( $(this).text() );
return false;
}
});
Method 2 - Explicitly bind events
$(document).ready(function(){
$('button').click(function(){
$('ol').append('<li class="dynamic-item"><a href="#">Dynamic list item</a></li>');
// After creating explicitly bind an event
$('a.dynamic-item:last').click(functino(){
alert( $(this).text() );
return false;
});
});
});
/*
* For AJAX loaded content bind it in success callback
*/
Notes and tips
Make sure you don't bind the same event to elements twice.
You should somehow separate newly created elements and then bind event in callback of an AJAX function. Here are two ways of separating AJAX loaded content:
- You can load into some specific container
<div id="ajax-loaded"></div>
and then apply your events to the elements inside that container (Ex:$('#ajax-loaded .my-button').click(...)
). - Add some class to your elements that are loaded on the server side.
Update: Added a demo page that shows how events are not bound in AJAX loaded elements here.
Hi -
ReplyDeleteCould you post a very simple code sample? I'm struggling with almost exactly this problem.
Thanks,
Eric
could you please give some example code for this issue. I'm trying to understand this because I have a problem in a project but I'm not sure if I do.
ReplyDeleteThank you !
ReplyDeleteThanks, just what I needed after spending two days on this problem!
ReplyDelete@Eric, @zekia I made a quick demo page that shows binding events to AJAX loaded elements here:
ReplyDeletehttp://custom-drupal.com/jquery-demo/bind-events-ajax/
@Codigo13, @Anonymous I am glad you found the tip useful.
Consider of the $.live(); instead
ReplyDeleteSimilarly, you could use .live() to add events to multiple div's in your ajax-loaded classed div
ReplyDeleteex:
$("#ajax-loaded .my-button").live("click", function()
{
//do something
});
i got headache to bind events with the help of jquery, and finally found helpful,
ReplyDeletemany thanks
=============
you can also have choice for .on() which adds handler to specific elements ,which is dynamicaly loaded.
ReplyDeletee.g.:
$("#ajax-loaded").on("click","#inner_element",function(event)
{
//do this
});