jQuery .on() Method: the Issue of Dynamically Added Elements

The jQuery .on() method is the recommended way for attaching events to any DOM element. This method attaches event handlers to a selected set of elements and it works perfectly for elements present on the page. However, there is an issue with the .on() method which inhibits the attachment of events to dynamically added elements. In this post, we will explore the issue of attaching events to dynamically added elements and then discuss the solution.

Problem Statement

First, let’s take a look at the issue with jQuery .on(). The following jQuery code attaches a click event to all div elements present on the page and when clicked, it logs the text in the browser console:

$(document).ready(function () {
    $('div').on('click', function() {
       console.log("Clicked.");
    });
});

This works great for all the div elements present on the page at the time of attaching the event. But the moment a div element is added dynamically (via code), the click event will not work. Consider the following jQuery code:

$(document).ready(function () {
   $('#btnAdd').on('click', function(){
    $('<div>Dynamic Div</div><br/>').appendTo('body');
  });
  $('div').on('click', function() {
    console.log("Clicked.");
  });
});

In the above jQuery code, there is a click event attached to a button which appends a div element to HTML body. And there is also a click event attached to all div elements. In this case, the click event will work properly for the div element present on the page while attaching and will not work for dynamically added div elements. You can check out a demo here.

In fact, for following jQuery code .on() works for even dynamically added elements. As here, before the event is attached, the dynamic div is already appended to the HTML body:

$(document).ready(function () {
  $('<div>Dynamic Div</div><br/>').appendTo('body');
  $('div').on('click', function() {
    console.log("Clicked.");
  });
});

And the official documentation also 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 now, let’s look at the solution.

Solution

In our problem statement, we attached the event directly to the DOM element. This is called Direct event. There is also something called delegate event, which doesn’t bind the event to the DOM elements, but it delegates the event from their parent. In other words, it’s the responsibility of the parent to execute the event on matching child elements. So in future, if new child elements are added to parent, parent will take care of executing the event on them. We shall see this with an example later on.

The jQuery on() method  has the following signature:

.on(events [, selector ] [, data ])

Here, the second parameter is “selector” which is optional. If this parameter is not present, then the event is called direct event (as used in our problem statement) and the presence of this parameter makes the event the delegate event. Delegated events have the advantage that they can process events from descendant elements that are added to the document at a later time. So to fix the problem, use the delegate event instead of the direct event.

See the following jQuery code:

$(document).ready(function () {
   $('#btnAdd').on('click', function(){
    $('<div>Dynamic Div</div><br/>').appendTo('body');
  });
  $(document).on("click","div", function(){
    console.log("Clicked.");
  });
});

In the above jQuery code, “div” as selector is passed and the click event is attached to document itself. So now when dynamic div elements are added to HTML, it’s the responsibility of the document to loop through all matching child elements and then execute the event. You can take a look at the demo here.

But be careful while using delegate events as it can degrade the performance if the parent is too far. For best results, attach delegated events at a document location as close as possible to the target elements. For example, the following HTML code has a div element and some child span elements:

<div id="dvParent">
    <span> Span 1 </span>
    <span> Span 2. </span>
</div>

And to attach click event to span element, we can use any of these codes:

  1. $('div#dvParent).on("click", "span", function () {
  2. $(document).on("click","div#dvParent span", function(){

But the issue with the second way is that the parent is very far from the target element as the document holds all DOM elements. So traversing will degrade the performance. Here, dvParent is close to span elements, so the first method gives the best results.

Conclusion

To sum things up, jQuery .on() method used as direct event will not work for dynamically added elements. Use delegate event via passing the selector parameter so that container takes the responsibility of executing the event in case of elements added in future dynamically.



Responsive Menu
Add more content here...