Closing on HTML click pattern, across large project - javascript

I figure other developers have run into this before. We have a large code base with many components following this pattern:
$('#elm').on('click',function($e){
$e.stopPropagation();
//... do stuff (i.e. Open something);
});
$('html').on('click',function($e){
//... do oposite of stuff (i.e. Close something);
}
Our issue is, all the stopPropagation's across the site are stopping closing of other components. What we really want is a mechanism to only block the click handler for this component but not for others.
I'm looking for a solution which is the easiest to implement right now to fix our bugs and for our Multi-developer team to follow in the future.

The .live() method handles events once they have propagated to the top of the document, it is not possible to stop propagation using live events this allows you to do hack and take advantage of this because the .live() method binds a handler to the $(document), and identifies which element triggered the event up at the top of the hierarchy using the event.target property.
The stopPropagation stops the propagation from bubbling up the DOM hierarchy, but since the handler is at the document level there is no upper place to propogate to.
On the other hand note that events handled by .delegate() will bubble up to the elements to which they are binded to; event handlers bound on any elements below it in the DOM tree will already have been executed by the time the delegated event handler is called. These handlers, therefore, may prevent the delegated handler from triggering by calling event.stopPropagation()
See this example: http://jsfiddle.net/knr3v/2/
Therefore you can use the live and delegate method instead of click, bind, on method to do exactly what you are explaining.

Related

Using $(body) to add single event listener on multiple elements [duplicate]

Where this is coming from
When I first learned jQuery, I normally attached events like this:
$('.my-widget a').click(function() {
$(this).toggleClass('active');
});
After learning more about selector speed and event delegation, I read in several places that "jQuery event delegation will make your code faster." So I started to write code like this:
$('.my-widget').on('click','a',function() {
$(this).toggleClass('active');
});
This was also the recommended way to replicate the behavior of the deprecated .live() event. Which is important to me since a lot of my sites dynamically add/remove widgets all the time. The above doesn't behave exactly like .live() though, since only elements added to the already existing container '.my-widget' will get the behavior. If I dynamically add another block of html after that code has ran, those elements will not get the events bound to them. Like this:
setTimeout(function() {
$('body').append('<div class="my-widget"><a>Click does nothing</a></div>');
}, 1000);
What I want to achieve:
the old behavior of .live() // meaning attaching events to not yet existent elements
the benefits of .on()
fastest performance to bind events
Simple way to manage events
I now attach all events like this:
$(document).on('click.my-widget-namespace', '.my-widget a', function() {
$(this).toggleClass('active');
});
Which seems to meet all my goals. (Yes it's slower in IE for some reason, no idea why?)
It's fast because only a single event is tied to a singular element and the secondary selector is only evaluated when the event occurs (please correct me if this is wrong here). The namespace is awesome since it makes it easier to toggle the event listener.
My Solution/Question
So I'm starting to think that jQuery events should always be bound to $(document).
Is there any reason why you would not want to do this?
Could this be considered a best practice? If not, why?
If you've read this whole thing, thank you. I appreciate any/all feedback/insights.
Assumptions:
Using jQuery that supports .on() // at least version 1.7
You want the the event to be added to dynamically added content
Readings/Examples:
http://24ways.org/2011/your-jquery-now-with-less-suck
http://brandonaaron.net/blog/2010/03/4/event-delegation-with-jquery
http://www.jasonbuckboyer.com/playground/speed/speed.html
http://api.jquery.com/on/
No - you should NOT bind all delegated event handlers to the document object. That is probably the worst performing scenario you could create.
First off, event delegation does not always make your code faster. In some cases, it's is advantageous and in some cases not. You should use event delegation when you actually need event delegation and when you benefit from it. Otherwise, you should bind event handlers directly to the objects where the event happens as this will generally be more efficient.
Second off, you should NOT bind all delegated events at the document level. This is exactly why .live() was deprecated because this is very inefficient when you have lots of events bound this way. For delegated event handling it is MUCH more efficient to bind them to the closest parent that is not dynamic.
Third off, not all events work or all problems can be solved with delegation. For example, if you want to intercept key events on an input control and block invalid keys from being entered into the input control, you cannot do that with delegated event handling because by the time the event bubbles up to the delegated handler, it has already been processed by the input control and it's too late to influence that behavior.
Here are times when event delegation is required or advantageous:
When the objects you are capturing events on are dynamically created/removed and you still want to capture events on them without having to explicitly rebind event handlers every time you create a new one.
When you have lots of objects that all want the exact same event handler (where lots is at least hundreds). In this case, it may be more efficient at setup time to bind one delegated event handler rather than hundreds or more direct event handlers. Note, delegated event handling is always less efficient at run-time than direct event handlers.
When you're trying to capture (at a higher level in your document) events that occur on any element in the document.
When your design is explicitly using event bubbling and stopPropagation() to solve some problem or feature in your page.
To understand this a little more, one needs to understand how jQuery delegated event handlers work. When you call something like this:
$("#myParent").on('click', 'button.actionButton', myFn);
It installs a generic jQuery event handler on the #myParent object. When a click event bubbles up to this delegated event handler, jQuery has to go through the list of delegated event handlers attached to this object and see if the originating element for the event matches any of the selectors in the delegated event handlers.
Because selectors can be fairly involved, this means that jQuery has to parse each selector and then compare it to the characteristics of the original event target to see if it matches each selector. This is not a cheap operation. It's no big deal if there is only one of them, but if you put all your selectors on the document object and there were hundreds of selectors to compare to every single bubbled event, this can seriously start to hobble event handling performance.
For this reason, you want to set up your delegated event handlers so a delegated event handler is as close to the target object as practical. This means that fewer events will bubble through each delegated event handler, thus improving the performance. Putting all delegated events on the document object is the worst possible performance because all bubbled events have to go through all delegated event handlers and get evaluated against all possible delegated event selectors. This is exactly why .live() is deprecated because this is what .live() did and it proved to be very inefficient.
So, to achieve optimized performance:
Only use delegated event handling when it actually provides a feature you need or increases performance. Don't just always use it because it's easy because when you don't actually need it. It actually performs worse at event dispatch time than direct event binding.
Attach delegated event handlers to the nearest parent to the source of the event as possible. If you are using delegated event handling because you have dynamic elements that you want to capture events for, then select the closest parent that is not itself dynamic.
Use easy-to-evaluate selectors for delegated event handlers. If you followed how delegated event handling works, you will understand that a delegated event handler has to be compared to lots of objects lots of times so picking as efficient a selector as possible or adding simple classes to your objects so simpler selectors can be used will increase the performance of delegated event handling.
Event delegation is a technique to write your handlers before the element actually exist in DOM. This method has its own disadvantages and should be used only if you have such requirements.
When should you use event delegation?
When you bind a common handler for more elements that needs same functionality. (Ex: table row hover)
In the example, if you had to bind all rows using direct bind, you would end up creating n handler for n rows in that table. By using delegation method you could end up handling all those in 1 simple handler.
When you add dynamic contents more frequently in DOM (Ex: Add/remove rows from a table)
Why you should not use event delegation?
Event delegation is slower when compared to binding the event directly to element.
It compares the target selector on every bubble it hits, the comparison will be as expensive as it is complicated.
No control over the event bubbling until it hits the element that it is bound to.
PS: Even for dynamic contents you don't have to use event delegation method if you are bind the handler after the contents get inserted into DOM. (If the dynamic content be added not frequently removed/re-added)
Apparently, event delegation is actually recommended now. at least for vanilla js.
https://gomakethings.com/why-event-delegation-is-a-better-way-to-listen-for-events-in-vanilla-js/
"Web performance #
It feels like listening to every click in the document would be bad for performance, but it’s actually more performant than having a bunch of event listeners on individual items."
I'd like to add some remarks and counterarguments to jfriend00's answer. (mostly just my opinions based on my gut feeling)
No - you should NOT bind all delegated event handlers to the document
object. That is probably the worst performing scenario you could
create.
First off, event delegation does not always make your code faster. In
some cases, it's is advantageous and in some cases not. You should use
event delegation when you actually need event delegation and when you
benefit from it. Otherwise, you should bind event handlers directly to
the objects where the event happens as this will generally be more
efficient.
While it's true that performance might be slightly better if you are only going to register and event for a single element, I believe it doesn't weigh up against the scalability benefits that delegation brings. I also believe browsers are (going to be) handling this more and more efficiently, although I have no proof of this. In my opinion, event delegation is the way to go!
Second off, you should NOT bind all delegated events at the document
level. This is exactly why .live() was deprecated because this is very
inefficient when you have lots of events bound this way. For delegated
event handling it is MUCH more efficient to bind them to the closest
parent that is not dynamic.
I kind of agree on this. If you are 100% sure that an event will only happen inside a container, it makes sense to bind the event to this container, but I would still argue against binding events to the triggering element directly.
Third off, not all events work or all problems can be solved with
delegation. For example, if you want to intercept key events on an
input control and block invalid keys from being entered into the input
control, you cannot do that with delegated event handling because by
the time the event bubbles up to the delegated handler, it has already
been processed by the input control and it's too late to influence
that behavior.
This is simply not true. Please see this codePen: https://codepen.io/pwkip/pen/jObGmjq
document.addEventListener('keypress', (e) => {
e.preventDefault();
});
It illustrates how you can prevent a user from typing by registering the keypress event on the document.
Here are times when event delegation is required or advantageous:
When the objects you are capturing events on are dynamically
created/removed and you still want to capture events on them without
having to explicitly rebind event handlers every time you create a new
one. When you have lots of objects that all want the exact same event
handler (where lots is at least hundreds). In this case, it may be
more efficient at setup time to bind one delegated event handler
rather than hundreds or more direct event handlers. Note, delegated
event handling is always less efficient at run-time than direct event
handlers.
I'd like to reply with this quote from https://ehsangazar.com/optimizing-javascript-event-listeners-for-performance-e28406ad406c
Event delegation promotes binding as few DOM event handlers as possible, since each event handler requires memory. For example, let’s say that we have an HTML unordered list we want to bind event handlers to. Instead of binding a click event handler for each list item (which may be hundreds for all we know), we bind one click handler to the parent unordered list itself.
Also, googling for performance cost of event delegation google returns more results in favor of event delegation.
When you're trying to capture (at a higher level in your document)
events that occur on any element in the document. When your design is
explicitly using event bubbling and stopPropagation() to solve some
problem or feature in your page. To understand this a little more, one
needs to understand how jQuery delegated event handlers work. When you
call something like this:
$("#myParent").on('click', 'button.actionButton', myFn); It installs a
generic jQuery event handler on the #myParent object. When a click
event bubbles up to this delegated event handler, jQuery has to go
through the list of delegated event handlers attached to this object
and see if the originating element for the event matches any of the
selectors in the delegated event handlers.
Because selectors can be fairly involved, this means that jQuery has
to parse each selector and then compare it to the characteristics of
the original event target to see if it matches each selector. This is
not a cheap operation. It's no big deal if there is only one of them,
but if you put all your selectors on the document object and there
were hundreds of selectors to compare to every single bubbled event,
this can seriously start to hobble event handling performance.
For this reason, you want to set up your delegated event handlers so a
delegated event handler is as close to the target object as practical.
This means that fewer events will bubble through each delegated event
handler, thus improving the performance. Putting all delegated events
on the document object is the worst possible performance because all
bubbled events have to go through all delegated event handlers and get
evaluated against all possible delegated event selectors. This is
exactly why .live() is deprecated because this is what .live() did and
it proved to be very inefficient.
Where is this documented? If that's true, then jQuery seems to be handling delegation in a very inefficient way, and then my counter-arguments should only be applied to vanilla JS.
Still: I would like to find an official source supporting this claim.
:: EDIT ::
Seems like jQuery is indeed doing event bubbling in a very inefficient way (because they support IE8)
https://api.jquery.com/on/#event-performance
So most of my arguments here only hold true for vanilla JS and modern browsers.

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

I am using a delegated event handler (jQuery) in my JavaScript code so stuff happens when dynamically added buttons are clicked.
I'm wondering are there performance drawbacks to this?
// Delegated event handler
$(document).on('click', '#dynamicallyAddedButton', function(){
console.log("Hello");
});
How would it compare to this, performance-wise?
// Regular event handler
$("#regularButton").on('click', function(){
console.log("Hello Again");
});
Looking at the jQuery documentation, it seems that events always bubble all the way up the DOM tree. Does that mean that the further nested an element is, the longer an event will take to work?
Edit: Is there a performance benefit to using JavaScript's event delegation rather than jQuery's? is asking a similar question, and the answer there is useful. I am wondering what the difference is between using a regular event handler and a delegated event handler. The linked questions make it seem like events are constantly bubbling up the DOM tree. With a delegated event handler does the event bubble up to the top and then back down to the specified element?
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 delegation on mouseover

When creating click events, I do my best to bind them only once – generally to a parent shared by all the nodes expected to trigger the event. I'm curious, however, what the best practice is with mouseover events: Does it still make sense to bind an event to a parent when the result would be the constant firing of the event on mouseover? What's the most efficient practice?
In order to provide some closure to this question, I'm going to paraphrase/quote some relevant notes from this answer: 'Should all jquery events be bound to $(document)?', which was referenced above by #Faust:
Event delegation does not always make your code faster. Unless you're binding to dynamic elements or a ton of elements, you should bind event handlers directly to the objects where the event happens as this will generally be more efficient.
More specifically, here are times when event delegation is required or advantageous:
When the objects you are capturing events on are dynamically created/removed and you still want to capture events on them without having to explicitly rebind event handlers every time you create a new one.
When you have lots of objects that all want the exact same event handler (where lots is at least hundreds). In this case, it may be more efficient at setup time to bind one delegated event handler rather than hundreds or more direct event handlers. Note, delegated event handling is always less efficient at run-time than direct event handlers.
When you're trying to capture (at a higher level in your document) events that occur on any element in the document.
When your design is explicitly using event bubbling and stopPropagation() to solve some problem or feature in your page.
Original answer by #jfriend00
So, I know this question is long dead, but I figured I might as well answer with a way to do this.
With dynamic-elements, you can establish a mousemove listener on the parent div/container, and then query within the div for elements with a :hover attribute.
For example:
<div class="list-container">
<ul class="dynamic-list-content">
<!-- actual list elements provided by js -->
</ul>
</div>
Then:
var listContainer = document.querySelector('.list-container');
listContainer.addEventListener('mousemove', function(e) {
var hovered = listContainer.querySelector('li:hover');
// do something with the hovered element here.
});
Note that (as you mentioned) this will fire a lot, but no more than if you added a mousemove event listener to the individual entries. And you could debounce this a bit, using data-attributes, unique ids, etc. From my tests though, it's pretty performant in Chrome.
you can also stop the propagation of events. More info here: http://api.jquery.com/event.stoppropagation/ and here event.preventDefault() vs. return false

Should all jquery events be bound to $(document)?

Where this is coming from
When I first learned jQuery, I normally attached events like this:
$('.my-widget a').click(function() {
$(this).toggleClass('active');
});
After learning more about selector speed and event delegation, I read in several places that "jQuery event delegation will make your code faster." So I started to write code like this:
$('.my-widget').on('click','a',function() {
$(this).toggleClass('active');
});
This was also the recommended way to replicate the behavior of the deprecated .live() event. Which is important to me since a lot of my sites dynamically add/remove widgets all the time. The above doesn't behave exactly like .live() though, since only elements added to the already existing container '.my-widget' will get the behavior. If I dynamically add another block of html after that code has ran, those elements will not get the events bound to them. Like this:
setTimeout(function() {
$('body').append('<div class="my-widget"><a>Click does nothing</a></div>');
}, 1000);
What I want to achieve:
the old behavior of .live() // meaning attaching events to not yet existent elements
the benefits of .on()
fastest performance to bind events
Simple way to manage events
I now attach all events like this:
$(document).on('click.my-widget-namespace', '.my-widget a', function() {
$(this).toggleClass('active');
});
Which seems to meet all my goals. (Yes it's slower in IE for some reason, no idea why?)
It's fast because only a single event is tied to a singular element and the secondary selector is only evaluated when the event occurs (please correct me if this is wrong here). The namespace is awesome since it makes it easier to toggle the event listener.
My Solution/Question
So I'm starting to think that jQuery events should always be bound to $(document).
Is there any reason why you would not want to do this?
Could this be considered a best practice? If not, why?
If you've read this whole thing, thank you. I appreciate any/all feedback/insights.
Assumptions:
Using jQuery that supports .on() // at least version 1.7
You want the the event to be added to dynamically added content
Readings/Examples:
http://24ways.org/2011/your-jquery-now-with-less-suck
http://brandonaaron.net/blog/2010/03/4/event-delegation-with-jquery
http://www.jasonbuckboyer.com/playground/speed/speed.html
http://api.jquery.com/on/
No - you should NOT bind all delegated event handlers to the document object. That is probably the worst performing scenario you could create.
First off, event delegation does not always make your code faster. In some cases, it's is advantageous and in some cases not. You should use event delegation when you actually need event delegation and when you benefit from it. Otherwise, you should bind event handlers directly to the objects where the event happens as this will generally be more efficient.
Second off, you should NOT bind all delegated events at the document level. This is exactly why .live() was deprecated because this is very inefficient when you have lots of events bound this way. For delegated event handling it is MUCH more efficient to bind them to the closest parent that is not dynamic.
Third off, not all events work or all problems can be solved with delegation. For example, if you want to intercept key events on an input control and block invalid keys from being entered into the input control, you cannot do that with delegated event handling because by the time the event bubbles up to the delegated handler, it has already been processed by the input control and it's too late to influence that behavior.
Here are times when event delegation is required or advantageous:
When the objects you are capturing events on are dynamically created/removed and you still want to capture events on them without having to explicitly rebind event handlers every time you create a new one.
When you have lots of objects that all want the exact same event handler (where lots is at least hundreds). In this case, it may be more efficient at setup time to bind one delegated event handler rather than hundreds or more direct event handlers. Note, delegated event handling is always less efficient at run-time than direct event handlers.
When you're trying to capture (at a higher level in your document) events that occur on any element in the document.
When your design is explicitly using event bubbling and stopPropagation() to solve some problem or feature in your page.
To understand this a little more, one needs to understand how jQuery delegated event handlers work. When you call something like this:
$("#myParent").on('click', 'button.actionButton', myFn);
It installs a generic jQuery event handler on the #myParent object. When a click event bubbles up to this delegated event handler, jQuery has to go through the list of delegated event handlers attached to this object and see if the originating element for the event matches any of the selectors in the delegated event handlers.
Because selectors can be fairly involved, this means that jQuery has to parse each selector and then compare it to the characteristics of the original event target to see if it matches each selector. This is not a cheap operation. It's no big deal if there is only one of them, but if you put all your selectors on the document object and there were hundreds of selectors to compare to every single bubbled event, this can seriously start to hobble event handling performance.
For this reason, you want to set up your delegated event handlers so a delegated event handler is as close to the target object as practical. This means that fewer events will bubble through each delegated event handler, thus improving the performance. Putting all delegated events on the document object is the worst possible performance because all bubbled events have to go through all delegated event handlers and get evaluated against all possible delegated event selectors. This is exactly why .live() is deprecated because this is what .live() did and it proved to be very inefficient.
So, to achieve optimized performance:
Only use delegated event handling when it actually provides a feature you need or increases performance. Don't just always use it because it's easy because when you don't actually need it. It actually performs worse at event dispatch time than direct event binding.
Attach delegated event handlers to the nearest parent to the source of the event as possible. If you are using delegated event handling because you have dynamic elements that you want to capture events for, then select the closest parent that is not itself dynamic.
Use easy-to-evaluate selectors for delegated event handlers. If you followed how delegated event handling works, you will understand that a delegated event handler has to be compared to lots of objects lots of times so picking as efficient a selector as possible or adding simple classes to your objects so simpler selectors can be used will increase the performance of delegated event handling.
Event delegation is a technique to write your handlers before the element actually exist in DOM. This method has its own disadvantages and should be used only if you have such requirements.
When should you use event delegation?
When you bind a common handler for more elements that needs same functionality. (Ex: table row hover)
In the example, if you had to bind all rows using direct bind, you would end up creating n handler for n rows in that table. By using delegation method you could end up handling all those in 1 simple handler.
When you add dynamic contents more frequently in DOM (Ex: Add/remove rows from a table)
Why you should not use event delegation?
Event delegation is slower when compared to binding the event directly to element.
It compares the target selector on every bubble it hits, the comparison will be as expensive as it is complicated.
No control over the event bubbling until it hits the element that it is bound to.
PS: Even for dynamic contents you don't have to use event delegation method if you are bind the handler after the contents get inserted into DOM. (If the dynamic content be added not frequently removed/re-added)
Apparently, event delegation is actually recommended now. at least for vanilla js.
https://gomakethings.com/why-event-delegation-is-a-better-way-to-listen-for-events-in-vanilla-js/
"Web performance #
It feels like listening to every click in the document would be bad for performance, but it’s actually more performant than having a bunch of event listeners on individual items."
I'd like to add some remarks and counterarguments to jfriend00's answer. (mostly just my opinions based on my gut feeling)
No - you should NOT bind all delegated event handlers to the document
object. That is probably the worst performing scenario you could
create.
First off, event delegation does not always make your code faster. In
some cases, it's is advantageous and in some cases not. You should use
event delegation when you actually need event delegation and when you
benefit from it. Otherwise, you should bind event handlers directly to
the objects where the event happens as this will generally be more
efficient.
While it's true that performance might be slightly better if you are only going to register and event for a single element, I believe it doesn't weigh up against the scalability benefits that delegation brings. I also believe browsers are (going to be) handling this more and more efficiently, although I have no proof of this. In my opinion, event delegation is the way to go!
Second off, you should NOT bind all delegated events at the document
level. This is exactly why .live() was deprecated because this is very
inefficient when you have lots of events bound this way. For delegated
event handling it is MUCH more efficient to bind them to the closest
parent that is not dynamic.
I kind of agree on this. If you are 100% sure that an event will only happen inside a container, it makes sense to bind the event to this container, but I would still argue against binding events to the triggering element directly.
Third off, not all events work or all problems can be solved with
delegation. For example, if you want to intercept key events on an
input control and block invalid keys from being entered into the input
control, you cannot do that with delegated event handling because by
the time the event bubbles up to the delegated handler, it has already
been processed by the input control and it's too late to influence
that behavior.
This is simply not true. Please see this codePen: https://codepen.io/pwkip/pen/jObGmjq
document.addEventListener('keypress', (e) => {
e.preventDefault();
});
It illustrates how you can prevent a user from typing by registering the keypress event on the document.
Here are times when event delegation is required or advantageous:
When the objects you are capturing events on are dynamically
created/removed and you still want to capture events on them without
having to explicitly rebind event handlers every time you create a new
one. When you have lots of objects that all want the exact same event
handler (where lots is at least hundreds). In this case, it may be
more efficient at setup time to bind one delegated event handler
rather than hundreds or more direct event handlers. Note, delegated
event handling is always less efficient at run-time than direct event
handlers.
I'd like to reply with this quote from https://ehsangazar.com/optimizing-javascript-event-listeners-for-performance-e28406ad406c
Event delegation promotes binding as few DOM event handlers as possible, since each event handler requires memory. For example, let’s say that we have an HTML unordered list we want to bind event handlers to. Instead of binding a click event handler for each list item (which may be hundreds for all we know), we bind one click handler to the parent unordered list itself.
Also, googling for performance cost of event delegation google returns more results in favor of event delegation.
When you're trying to capture (at a higher level in your document)
events that occur on any element in the document. When your design is
explicitly using event bubbling and stopPropagation() to solve some
problem or feature in your page. To understand this a little more, one
needs to understand how jQuery delegated event handlers work. When you
call something like this:
$("#myParent").on('click', 'button.actionButton', myFn); It installs a
generic jQuery event handler on the #myParent object. When a click
event bubbles up to this delegated event handler, jQuery has to go
through the list of delegated event handlers attached to this object
and see if the originating element for the event matches any of the
selectors in the delegated event handlers.
Because selectors can be fairly involved, this means that jQuery has
to parse each selector and then compare it to the characteristics of
the original event target to see if it matches each selector. This is
not a cheap operation. It's no big deal if there is only one of them,
but if you put all your selectors on the document object and there
were hundreds of selectors to compare to every single bubbled event,
this can seriously start to hobble event handling performance.
For this reason, you want to set up your delegated event handlers so a
delegated event handler is as close to the target object as practical.
This means that fewer events will bubble through each delegated event
handler, thus improving the performance. Putting all delegated events
on the document object is the worst possible performance because all
bubbled events have to go through all delegated event handlers and get
evaluated against all possible delegated event selectors. This is
exactly why .live() is deprecated because this is what .live() did and
it proved to be very inefficient.
Where is this documented? If that's true, then jQuery seems to be handling delegation in a very inefficient way, and then my counter-arguments should only be applied to vanilla JS.
Still: I would like to find an official source supporting this claim.
:: EDIT ::
Seems like jQuery is indeed doing event bubbling in a very inefficient way (because they support IE8)
https://api.jquery.com/on/#event-performance
So most of my arguments here only hold true for vanilla JS and modern browsers.

When using jQuery on(), why use (document) vs. the element itself?

I'd like a jQuery expert in their own words to explain why the $(document) identifier is recommended by others for jQuery's on() statement vs just using an element itself
Example 1: Why is using $(document) here better then Example #2?
$(document).on("click", ".areaCodeList a", function(){
// do stuff
});
Example 2: Why is using the element this way considering not good practice compared to Example 1?
$(".areaCodeList a").on("click", function(){
// do stuff
});
Both of those are valid.
The former works for dynamically added elements. You use document because you're delegating events on children of the document object, so events bubble up to the document level. It's also more convenient to select the closest parent you can (and the parent must exist on the page at load).
The latter still works, and is a preferred way to simply bind events to specific elements.
I personally don't recommend delegating through the document object, but rather the closest parent that exists on page load.
Here are the docs for on().
This is not true.
Those two lines do totally two different things.
The first one is a delegate event with the selector of ".areaCodeList a" while the second line is an event attached to the ".areaCodeList a" elements.
A delegate event will fire to every ".areaCodeList a" element although it was in the DOM when that line executed.
Anyway, attaching delegate events to the document isn't recommended at all. as written in the live docs:
Since all .live() events are attached at the document element, events take the longest and slowest possible path before they are handled
Please read the on docs:
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(). To ensure the elements are present and can be selected, perform event binding inside a document ready handler for elements that are in the HTML markup on the page. If new HTML is being injected into the page, select the elements and attach event handlers after the new HTML is placed into the page. Or, use delegated events to attach an event handler, as described next.
Delegated events have the advantage that they can process events from
descendant elements that are added to the document at a later time. By
picking an element that is guaranteed to be present at the time the
delegated event handler is attached, you can use delegated events to
avoid the need to frequently attach and remove event handlers. This
element could be the container element of a view in a
Model-View-Controller design, for example, or document if the event
handler wants to monitor all bubbling events in the document. The
document element is available in the head of the document before
loading any other HTML, so it is safe to attach events there without
waiting for the document to be ready.
...
...
I think you are confusing a few concepts. It is not recommended to bind to the document element, however there are times when you want to do so e.g when binding events to dynamically added elements.
All this may seem unclear, so here is the first example which uses the class selector directly and binds a click event, the element is inserted dynamically later after the event is bound. As you can see the event never gets fired because we selected an element that was not present in the DOM at the time the event was bound. This is equivalent to a .click
Now look at this second example. Here you see we are defining the root element as the document. Which means that the click event will bubble all the way up the DOM tree and then fire if the element that was clicked has a class dynamic. This is equivalent to .live
Now, if in example one, at the time of binding the event the element was present in the DOM, the code would work just fine, as you can see here.
That being said. Here's an except from the docs which clarifies the behavior above.
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, in conclusion. Use the document element when you are sure that there's no parent element for the element you are selecting that's guaranteed to be in the DOM at the time th event is bound. If there is a parent element that is present then use that instead of the document element. This way the event will not have to bubble all the way up the document, it needs to travel only a short distance.
There's nothing "recommended" about this. The first snippet sets up a "delegated" event, the latter is a "direct" one. The documentation for on() explains these in depth.
Delegated events are necessary when you need to listen to events for elements that don't exist yet - ones that will be created dynamically after, for example, an AJAX call. They can also sometimes be better performance-wise - you need less memory to attach a "real" event handler to the document object, than to 1000 buttons.
I'd say it's still preferrable to use direct event handlers when you can, or attach delegate events to an element as close to the real event sources as you can. Having all event handlers on the document object is probably terrible for performance - you have to match every event fired against all the selectors. It's also probably needed if you need to stop an event from bubbling - if all events are caught on the document, they've already bubbled as far as they'll go.
Actually the best solution in a case such as this is not using the $(document) neither the specific element like $("selector").
The best approach is using the container of the element and bind the element selector in the on function. By doing this you avoid unnecessary event bubbling to the document.
So the code should look like:
$("this_is_the_container").on('event','my_element_selector',function(){
// do some stuff here
})
$(*selector*).on(*event*, function(){})
will apply only for elements which is already load in page at the moment of script run. If in future, will appear new elements, the event handler will be not work.
$(document).on(*event*, *selector*, function(){}
will execute event handler even the elements with same selector will appear on page after script run.
So, if you have some elements, which can appear after in random time, use
$(document).on()
else use
$(*selector*).on();

Categories