Thursday, April 18, 2013

Direct vs Delegated Events with jQuery on() method

jQuery added a new method called  on()  in release 1.7 which provides all functionality for attaching event handlers. And it has made other event handler event like live() and delegate() dead. This method was introduced due to performance issue with live() method.

Related Post:

There are 2 signaturs of  on()  method.
  1. .on(events[,selector] [,data ],handler(eventObject))
  2. .on(events[,selector] [,data])
In this post, we will focus on the one of the optional parameter named "selector". The official jQuery documentation about this parameter says,

" A selector string to filter the descendants of the selected elements that trigger the event. If the selector is null or omitted, the event is always triggered when it reaches the selected element."

As this is an optional parameter, so it can be omitted. The present/absence of this parameter defines Direct Event or Delegated Event. If it is absent, then it is called "Direct event", otherwise "Delegated event". First, we will see what Direct event means.

For example, there are 2 div element present on the page,
<div>First Div</div>
<div>Second Div</div>
And attach a click event on div element using on() method, which just alerts the clicked element text.
$(document).ready(function () {
    $('div').on('click', function() {
        alert("Clicked: " + $(this).html());
    });
});
So the above jQuery code instructs "HEY!!!! I want every div to listen to click event. And when you are clicked , give me alert." Now, this is absolutely fine and works like charm. This is called "Direct Event". But there is an issue with Direct events. Official jQuery document about Direct events says,

" 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()."

So for Direct events to work, the element has to present on the page. If it is added dynamically, it's not going to work.

Consider the same example, which now on click event adds a div element dynamically, after showing alert. But the click event don't get attached to dynamically added elements.
$(document).ready(function () {
    $('div').on('click', function() {
        alert("Clicked: " + $(this).html());
        $('<div>Dynamic Div</div>').appendTo('body');
    });
});
So when dynamically added div elements are clicked, nothing happens! As the event is not attached to them.
So, what is the solution now? Well, solution is to use "Delegated events". Delegated events have the advantage that they can process events from descendant elements that are added to the document at a later time.

When a selector is provided, the event handler is referred to as delegated. So let's modify the above jQuery code to have "Delegated event" instead of "Direct event".
$(document).ready(function () {
    $(document).on("click","div", function(){
        alert("Clicked: " + $(this).html());
        $('<div>Dynamic Div</div>').appendTo('body');
    });
});
So the jQuery code instructs "HEY!! document when your child element which is div gets clicked, give me alert and append a dynamic div to body." Using delegated event, you will find the same behavior for already present and dynamically added elements.
There is another advantage of using delegated event which is "much lower overhead". For example, On a table with 1,000 rows in its tbody, below code attaches a handler to 1,000 elements.
$("#Table tbody tr").on("click", function(event){
  alert($(this).text());
});
On the other side, using "Delegated event", attaching event handler to only one element which is tbody. The event only needs to bubble up one level (from the clicked tr to tbody).
$("#Table tbody").on("click", "tr", function(event){
  alert($(this).text());
});
Let's take another example, to explain "Delegated events". Consider following Html code.
<div id="container"> 
    <span> Span within Container div. </span>
    <br/> <span> Another span within Container div. </span>
</div>
And below is jQuery code to attach (delegated) click event on span element within div with ID "container" is,
$(document).ready(function () {
  var iCount = 1;
  $('div#container').on("click", "span", function () {
    alert("Clicked: " + $(this).html());
    var $dynSpan = $('<br/><span> Dynamic Span ' + iCount + '.</span>');
    $dynSpan.appendTo('div#container');
    iCount++;
  });
});
Here is another important tips "Attaching many delegated event handlers near the top of the document tree can degrade performance". For best performance, attach delegated events at a document location as close as possible to the target elements as done in above jQuery code. So instead of $(document).on("click","div#container span", function(){ , use $('div#container').on("click", "span", function () {

Feel free to contact me for any help related to jQuery, I will gladly help you.