The tricky thing about replacing instances of jQuery’s live() with the on() method

There are a number of ways to bind event handlers to elements with jQuery, a popular one called live() is deprecated since version 1.7 so people are advised to replace this in their code. The live() method was great for committing event handlers to elements that were dynamically inserted after page load. Now the right method to use is on() which is relatively new and its benefits have been discussed at length. Just like live(), on() is able to attach event handlers to newly inserted elements, perfect right? But there’s a bit of a gotcha.

Converting instances of live() to on() is going to be straightforward most of the time (see how), but you still may run into trouble. A potential issue has to do with how event delegation is dealt with. Event delegation is important to take advantage of because it replaces the need to add a multitude event handlers attached to separate elements by applying one event handler to a single parent element. When assigning event handlers in this way – say, because you want to add some click hanlders to buttons inside the rows of a table – you usually pick the most efficient parent element. The broader the parent element, the less efficient and less robust things become, so it is important to make the parent element as near as possible.

If you are adding event handlers inside a table, the obvious choice might be the table element.

$('table').on("click", "a.addrow", function(){
  // do stuff
});

That will work fine if this table element was present on page load. But what if the table was added dynamically? And this is where you might get confused (I certainly did). The thing is, it won’t work if your parent element was dynamically added. It has to be an element that was present on page load.

The problem:
When setting the parent element with the on() method, this element must be present on the page, elements added later are ignored.

Solutions

Applying event delegation to a broader parent element

One easy solution is to set a broader parent element that you know for sure exists after page load has finished. There can be some drawbacks to this which are mostly relevant in situations where you do not control all the elements on the page to start with. In these case you might be tempted to append the event handler to something like the body element, because you can be certain this element will be on the page. But the broader the element, the more likely it is that something else could prevent your event handler firing. If the environment has a lot of other scripts running, the chances increase that a conflict might occur. This is especially true if you are coding something like a WordPress plugin and have to contend with dozens of other scripts you can’t control.

In the best case scenario, you pick an element with a specific ID that is one level higher up the dom. This is going to be more efficient than say, a class name or something higher up the DOM.

<div id="tablewrap">
 // table would later be inserted here
</div>
$('#tablewrap').on("click", "table a.addrow", function(){
  // do stuff
});

Attaching event handlers when elements are inserted

The second solution involves setting the event handlers when the relevant elements are inserted. There are a couple of possible scenarios that may apply and a few ways to implement this. I’ll cover one or two to illustrate the general idea.

So let’s say groups of elements are being inserted into the page with jQuery’s .clone() method and you want the new clone to have the same event handlers. If this is your own code doing so, you can easily opt to do a deep clone and copy all of the event handlers. This way all the event handlers that apply to the element that is being cloned will apply to the clone. This is as easy as setting a few parameters:

instead of using .clone() without parameters set, use .clone(true, true) and all your event handlers will apply to the new clone!

When you can’t edit the code of the script that is inserting elements

Maybe you can’t change the code (for example, to add the parameters to the clone method) because it belongs to a script you can’t edit. In this case you will have to figure out another way to bind the event handlers on insertion. The best thing is to inspect what the code looks like and see where you can hook in. Often a custom event is triggered when elements are being inserted. If this is the case, you can piggy back on that custom event.

// the addTable event triggers when a new table is inserted into the DOM
$('#interface').on("addTable", function(){
   // now we can add our event handler
   $("table").on("click", "a.delete-table", function(){
     // do stuff
     $(this).closest('table').remove();
   }
});

An even better variation of this is when the triggered event passes the element as an argument. That way you can target that element and append your event handlers without the extra legwork.

// demo link here http://codepen.io/PeterKnight/pen/oknre

$("#interface").on("click", ".add-table", function(){
    var $el = $('<table></table>');
  $el.insertAfter('button')
      .append('New table!<a href="#" class="delete-table">DELETE THIS TABLE, DO IT NOW!</a>')
      .trigger("addTable", $el);
});

$("#interface").on("addTable", function(e, el){
   $(el).on("click", "a.delete-table", function(){
     // do stuff
     e.preventDefault();
     $(this).closest('table').remove();
   });
});

Execute a function or method that binds the event handler after the element has been inserted

This solution is really simple a straightforward. Basically you write a function or method that appends the event handlers to the desired element and you execute this function or method after the element has been inserted into the page. A very rudimentary example (not copy paste worthy!):

// function or code that inserts element
someFunctionThatInsertsAnElement();
// function that appends event handlers
someFunctionThatAppendsEventHandlers(element);

// above function could look a bit like this
function someFunctionThatAppendsEventHandlers(element){
   jQuery(element).on("click", "a.addRow", function(){
// add a row etc.
});
}

A Ninja CSS Trick

There is a very clever and effective way of detecting a newly inserted element with the help of CSS animations. This technique doesn’t require jquery but it only works in browsers that support keyframe, which all modern browsers do…of course IE6-9 aren’t party to that. The neat thing about this method is that it is great in situations where you don’t how your script is going to be used, while you can still reliably and efficiently can set a suitable parent element to use with on().

I have created a blunt demo that demonstrates the technique on codepen, be sure to read the original article linked above though!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Current day month ye@r *