jQuery: Bind events to AJAX loaded / dynamically created elements

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:

  1. 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.
  2. 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:

  1. 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(...)).
  2. 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.