Event Delegation VS Direct Binding When Adding Complex Elements to a Page

Event delegation vs direct binding when adding complex elements to a page

You will create less CPU overhead in binding the events using $(<root-element>).on(<event>, <selector>) since you will be binding to a single "root" element instead of potentially many more single descendant elements (each bind takes time...).

That being said, you will incur more CPU overhead when the actual events occur as they have to bubble up the DOM to the "root" element.

Short-story: delegate saves CPU when binding event handlers; bind saves CPU when events trigger (e.g. a user clicks something).

So it's up to you to decide what point is more important for performance. Do you have available CPU when you add the new elements? If so then binding directly to the new elements would be the best for overall performance however if adding the elements is a CPU intensive operation you will probably want to delegate the event binding and let the event triggering create some extra CPU overhead from all the bubbling.

Note that:

$(<root-element>).on(<event>, <selector>, <event-handler>)

is the same as:

$(<root-element>).delegate(<selector>, <event>, <event-handler>)

and that:

$(<selector>).on(<event>, <event-handler>)

is the same as:

$(<selector>).bind(<event>, <event-handler>)

.on() is new in jQuery 1.7 and if you are using 1.7+ then .delegate(<selector>, <event>, <event-handler>) is just a short-cut for .on(<event>, <selector>, <event-handler>).

UPDATE

Here is a performance test showing that it is faster to delegate event binding than to bind to each element individually: http://jsperf.com/bind-vs-click/29. Sadly this performance test has been removed.

UPDATE

Here is a performance test showing that event triggering is faster when you bind directly to elements rather than delegate the binding: http://jsperf.com/jquery-delegate-vs-bind-triggering (Note that this isn't a perfect performance test since the binding methods are included in the test, but since delegate runs faster on binding it just means that bind is even faster relatively when talking about triggering)

Difference between delegating events to different elements in the DOM

There is no functional difference between the two. There is a (very) slight performance difference, though, as the first example requires the event to bubble up the DOM from the #toggleList element to the document. The second example needs only go as far up the DOM as the #toggleArea, so will be slightly faster.

The old live() method used to use the first method you outlined and was deprecated because of it.

In an ideal world, the delegated event should be assigned to the nearest parent element to those being dynamically created which is available in the DOM when document.ready fires. Avoid assigning it to document where possible.

Different ways of event binding jquery

The performance of using delegated events will not always necessarily be better than directly attaching events.

Consider the following markup:

<div id="someId">   
<div class="someParent">
<div class="someElement"></div>
</div>
<div class="someParent">
<div class="someElement"></div>
</div>
<div class="someParent">
<div class="someElement"></div>
</div>
</div>

There'd be no point in attaching handlers to someParent, delegating someElement, as you'd be attaching the same number of handlers if you attached the handler to someElement directly:

$('div.someParent').on('click', 'div.someElement', function() {
// given the example markup, this would be slightly slower than
// attaching the element directly $('div.someElement').on('click', ..
// as you are attaching the same number of handlers, but have the overhead
// of filtering for descendents with the class `div.someElement`
});

If the markup looked like this:

<div id="someId">
<div class="someParent">
<div class="someElement"></div>
<div class="someElement"></div>
<div class="someElement"></div>
<div class="someElement"></div>
<div class="someElement"></div>
</div>
<div class="someParent">
<div class="someElement"></div>
<div class="someElement"></div>
<div class="someElement"></div>
<div class="someElement"></div>
<div class="someElement"></div>
</div>
<div class="someParent">
<div class="someElement"></div>
<div class="someElement"></div>
<div class="someElement"></div>
<div class="someElement"></div>
<div class="someElement"></div>
</div>
</div>

.. and you delegated the handler like above, you'd be attaching 3 handlers, as opposed to 15 (if you'd attached the handler directly), which would perform better.

In either case, the best way would be to use the closest parent element with an id, so you're only attaching a single handler:

$('#someId').on('click', 'div.someElement', function() {
// do some stuff
});

The other benefit of delegating handlers (as you already know), is that it will be triggered for dynamically added elements.

Have a look at this test on jsperf.com

Are there performance drawbacks to using a delegated event handler in JavaScript and jQuery?

Every time you click pretty much anywhere in the document, the event is going to be manually bubbled up to the document element (after the natural bubbling takes place) and will run the selector engine for every element between the clicked element and the document.

So if you click on an element nested 20 elements deep, the selector engine will run 20 times for that one click.

Furthermore, this will happen for every selector the document has. So if you give it 20 selectors and click 20 elements deep, the selector engine has to run 400 times for that one click. (The manual bubbling happens only once, of course.)

Selector-based delegation is fine, but try to keep it closer to the targeted element(s) if possible.

Event binding on dynamically created elements?

As of jQuery 1.7 you should use jQuery.fn.on with the selector parameter filled:

$(staticAncestors).on(eventName, dynamicChild, function() {});

Explanation:

This is called event delegation and works as followed. The event is attached to a static parent (staticAncestors) of the element that should be handled. This jQuery handler is triggered every time the event triggers on this element or one of the descendant elements. The handler then checks if the element that triggered the event matches your selector (dynamicChild). When there is a match then your custom handler function is executed.


Prior to this, the recommended approach was to use live():

$(selector).live( eventName, function(){} );

However, live() was deprecated in 1.7 in favour of on(), and completely removed in 1.9. The live() signature:

$(selector).live( eventName, function(){} );

... can be replaced with the following on() signature:

$(document).on( eventName, selector, function(){} );

For example, if your page was dynamically creating elements with the class name dosomething you would bind the event to a parent which already exists (this is the nub of the problem here, you need something that exists to bind to, don't bind to the dynamic content), this can be (and the easiest option) is document. Though bear in mind document may not be the most efficient option.

$(document).on('mouseover mouseout', '.dosomething', function(){
// what you want to happen when mouseover and mouseout
// occurs on elements that match '.dosomething'
});

Any parent that exists at the time the event is bound is fine. For example

$('.buttons').on('click', 'button', function(){
// do something here
});

would apply to

<div class="buttons">
<!-- <button>s that are generated dynamically and added here -->
</div>

What's the difference between `on` and `live` or `bind`?

on() is an attempt to merge most of jQuery's event binding functions into one. This has the added bonus of tidying up the inefficiencies with live vs delegate. In future versions of jQuery, these methods will be removed and only on and one will be left.

Examples:

// Using live()
$(".mySelector").live("click", fn);

// Equivalent `on` (there isn't an exact equivalent, but with good reason)
$(document).on("click", ".mySelector", fn);
// Using bind()
$(".mySelector").bind("click", fn);

// Equivalent `on`
$(".mySelector").on("click", fn);
// Using delegate()
$(document.body).delegate(".mySelector", "click", fn);

// Equivalent `on`
$(document.body).on("click", ".mySelector", fn);

Internally, jQuery maps all these methods and shorthand event handler setters to the on() method, further indicating that you should ignore these methods from now on and just use on:

bind: function( types, data, fn ) {
return this.on( types, null, data, fn );
},
live: function( types, data, fn ) {
jQuery( this.context ).on( types, this.selector, data, fn );
return this;
},
delegate: function( selector, types, data, fn ) {
return this.on( types, selector, data, fn );
},

See https://github.com/jquery/jquery/blob/1.7/src/event.js#L965.

Understanding jQuery on method regarding relative selector (event delegation)

You got it right, It is better to find the closest parent that exist in DOM at the time of event delegation. Using closest parent limits the range jQuery has to check before delegating.

For ex: Lets say we bind a click handler to body tag delegating for #trigger element that will be dynamically included later

$('body').on('click', '#trigger', function() {
// an awesome click event
});

$('body').append('<span id="trigger">Click</span>');

In the above example, jQuery will bind the click handler to body tag and internally check if the clicked element matches the selector. Note that every time anywhere you click in the body, the jQuery .on is executed which validates if the click event is triggered from #trigger elemenet and calls the event handler.

This is why it is better to find and delegate the event to the closest parent that exist in DOM at the time of binding.

With that being said, an even better approach would be to directly bind the event after the element is injected to DOM. Read more on which method to choose here: event delegation vs direct binding when adding complex elements to a page



Related Topics



Leave a reply



Submit