Why does Event Delegation save memory? - javascript

I tried to understand how 'addEventListener' works like when we add event, where does it save? DOM tree? Or certain memory place?. But couldn't find answer, all the posts I've ever seen said "You can't", because there's no standard for it and every browser has different implementation.
So I was trying to skip it, but got a question about event delegation. In many posts, people say event delegation saves memory, because we don't have to attach event listener to each components. But I thought, I have to understand how addEventListener works to understand why event delegation saves memory.
So what's the reason event delegation is memory efficient?

Somewhere in the browser's memory, there's a data structure that contains the list of event listeners for each element. If you call addEventListener() separately for 100 different elements, it will create 100 entries in this table.
But if you use event delegation, you only have to call addEventListener() once, so there's only 1 entry in the table.
You can actually see a representation of this data by opening the Event Listeners tab in the Elements panel of Developer Tools.
However, the amount of memory you save is probably not very significant. Each listener is probably just a couple of pointers, one to something representing the event type (click, change, etc.) and another to the callback function. If all 100 event listeners call the same function, there's just one function object and 100 pointers to it. If the function is a closure, there will also be an environment object containing the variables it closes over, which will add a little more memory use, but not very much.
On the other hand, when you use delegation, the callback function needs to do extra work to determine if the event target is an appropriate nested element. This makes it a little slower. It will also be called if the event is triggered on an element that's in the container element but not one of the elements you're delegated to (and will run repeatedly as the event bubbles out), so the function is run more often. If memory were really at a premium, this would be a classic time/space tradeoff.
In practice, delegation isn't used to save memory, it's used to simplify the design. It's most often used when you're adding elements to the DOM dynamically, or changing attributes (e.g. class names) that the event binding depends on. Delegation allows you to define the event listener once, rather than having to add or remove it from elements as they're added or modified.

Related

Event Listeners Efficiency

How do event listeners in any programming language actually work internally?
The point of this post is to get an overall idea of how event listeners in general work. A while back, when I first started playing with Javascript and Html, every time I needed to create a button to perform an action, lets say execute function 'my_func();', I would simply go...
<button onclick="my_func();">Click me</button>
That is just wonderful, a button, staying idle for the majority of the time, except when clicked. Then, it would execute the corresponding function.
Then I came across Event Listeners. Basically, an event listener is a procedure or function in a computer program that waits for an event to occur.
The first thing that came into my mind was the following: When you create an event listener, does that basically just create an infinite loop that checks whether something has been triggered or not? Isn't this inefficient if you have dozens or hundreds of events to listen for? Hundreds of loops checking for a different specific condition every second? Why would you use this instead of the beautiful 'onclick'?
I will proceed to post the answers that I found, (not in StackOverflow, that's why I decided to post it here, so as to enlarge the already vast content of this site). This answer, of all the ones I read, was the one that convinced me the most. However, if you know something that you think would add to the topic, feel free to share your knowledge (the more the better).
The core question was the following: Isn't this inefficient if you have dozens or hundreds of events to listen for?
And the truth is, if that was the case, it would be really inefficient. That is why it doesn't work that way.
The program (in the case of Java), or browser (in the case of Javascript), receives events from the operating system every time something happens — when the mouse is moved, when a key is pressed, when the window is resized, when a timer event expires, and so on. For each of those events, the browser needs to figure out if an event handler needs to be dispatched. For example, on a mouse left button down event, it needs to take the coordinates of the mouse and figure out what elements are underneath it, and then check if there are any event listeners registered for those events, and if so add them to the event queue to be executed the next time the engine becomes free.
Once again, feel free to add information, or correct me if you think there is something wrong or somehow inaccurate.
There's very little difference between running an event handler from onclick and from addEventListener. In both cases, it simply attaches the handler to the DOM element in question. The only significant difference between them is that you can only have one onclick attribute, but every time you call addEventListener() it adds to the list of listeners on that element -- internally there's an array of listeners that addEventListener() pushes onto.
But the way these event handlers are processed is essentially the same. When a click event is sent to an element, the browser looks up its onclick attribute and list of click listeners, and executes all of them. There's no difference in efficiency between them. The only infinite loop is the browser's main event loop, which is processing all events that are received from the OS, finding the appropriate DOM elements, seeing if they have handlers for that event, and calling the handlers.
As what others have said, internally there is no difference between the two. But using the addEventListener() method you can easily attach multiple functions on a single event rather than going through concatenations when you have lots of scripts.
with addEventListener()
Script 1
myEle.addEventLister('click', myFunc);
Script 2
myEle.addEventLister('click', myFunc2);
You see how easy it is to attach functions on an element's event.
using setAttribute()
Script 1
myEle.setAttribute('onclick', myFunc);
Script 2
myEle.setAttribute('onclick', myEle.getAttribute('onclick') + myFunc2);
The extra code is a bit of a hassle
For practical purposes, there is no difference between the 'onclick' and the 'EventListener' attributes. All in all, that's just what they are, attributes that you add or remove from a specific object. As a consequences, since they are attributes, not mere methods, they do not loop themselves, but instead serve as parameters for the "general event loop" that constantly occur in your window. Hence the lack of difference in efficiency.
However, if you have a ton of event listeners, or onclick attributes on a ton of different objects, it may reduce the overall execution speed, as the general loop has to go through more elements to check, for a specific occurred event, if there is anything listening to it; but this happens indistinctelly of how you address your events (with a listener, onclick attributes, etc.)
So, I will conclude by saying that there is no practical difference in the way these event handlers are processed. When a particular event is sent to an element, the browser looks up on its attributes and/or list of listeners related to that particular event, and executes each one of them. There's no difference in efficiency between them. The only infinite loop is the "general window loop" or the main event loop, which is processes all the events that happen, and looks for the appropriate object to check for any handlers related to that event, and should any be found, it calls the function attached to them.

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

Does having a large number of event handlers degrade performace?

I have a page with some 30+ event handlers.
Similar to this:
$section.on("click", "a.unselect-all", function () {
var $s = $(this).siblings("select");
$s.find("option").removeAttr("selected");
$s.val("");
});
The page itself is not all that large.
But I feel that it's response time has become more sluggish with more event handlers.
First off, here's a similar question/answer with some pertinent info to understanding this issue: Should all jquery events be bound to $(document)?.
Here are some notes on large numbers of event handlers and performance:
30+ is NOT a large number of event handlers. In most cases, you shouldn't have to worry about 30+ event handlers at all (see possible exception below if you have nasty selectors).
If event handlers are attached directly to the intended object (the most efficient way for them to be processed at the actual event time), you will maximize your run-time performance.
If you are using delegated event handling, then event handlers should be placed on a parent that is as close as possible to the actual object receiving the event. Putting all delegated event handlers on a top level object like document or document.body is generally a bad idea because that makes the code have to look at more selectors for every event than if the event handlers are closer to the actual object that triggers the event thus avoiding evaluating most of the selectors for most events.
If you are using delegated event handling, then the performance of the secondary selector (the 2nd argument to .on()) is important. If this is a complicated selector that is not fast to evaluate and you have a lot of these all attached to the same object for the same event, you could start to see some performance degradation. How much performance degradation will depend upon the exact circumstances.
So, the worst case is if you have a lot of delegated event handlers all attached to the same object that all have a complicated secondary selector. This is the worst case because the event will have to bubble up to this parent object and then jQuery will have to go through every one of the delegated events for this particular event and it will have to evaluate the secondary selector argument to see if it matches the target object. If you have a lot of these delegated event handlers on the same object and you the secondary selector is somewhat complicated, it's possible that the time to process all this could start to be noticeable.
As with any performance issue, if this is really important to you, then you need to do your own performance testing. Install 30 delegated 'click' handlers with secondary selectors like you will be using to the document object in a sample page and try it. See if you can see any performance degradation at all between the click and the response to the click. Try it with 100 event handlers, 1000 event handlers, etc... until you see where it becomes noticeable.

What makes binding slow?

Is it the process of doing the binding, or the having many things bound that's the primarily issue of binding more events than necessary?
The answer's probably both, but to what extent?
Also, I would assume that mouseover events are more expensive than click events, since they have to be checked for more frequently. Right?
The binding of events does take time, so if you bind say, a hundred or more events, user interaction with the browser will be 'uneventful' during the time spent binding all of those events.
The more event handlers on the page, the longer the event queue, the slower the UI.
#Juan nicely summarises event delegation in a single sentence in his answer, as an alternative to binding events to many child elements.
As far as I have noticed, the more listeners you add, the slower the UI will be. Event delegation uses less memory; instead of a listener for each child node, you have a single, smarter handler at a parent element. Less memory, less attaching and detaching handlers.
Mouseover events are not necessarily more expensive, it's not extra memory, it's just that your handler is run very often, so you need to make sure it's light code

jquery unbinding events speed increases

I have a big content slideshow kinda page that I'm making that is starting to use a lot of event triggers. Also about half of them use the livequery plugin.
Will I see speed increases by unloading these events between slides so only the active slide has bound events?
Also is the native livequery significantly faster then the livequery plugin?(cause it's certainly less functional)
Also would something like this:
http://dev.jquery.com/attachment/ticket/2698/unload.js
unbind livequery events as well?
I really just need to know how long it takes to unload/load an event listener vs how many cycles they are really eating up if I leave them running. Also any information on live events would be awesome.
I need more details to offer actual code, but you might want to look into Event Delegation:
Event delegation refers to the use of a single event listener on a parent object to listen for events happening on its children (or deeper descendants). Event delegation allows developers to be sparse in their application of event listeners while still reacting to events as they happen on highly specific targets. This proves to be a key strategy for maintaining high performance in event-rich web projects, where the creation of hundreds of event listeners can quickly degrade performance.
A quick, basic example:
Say you have a DIV with images, like this:
<div id="container">
<img src="happy.jpg">
<img src="sad.jpg">
<img src="laugh.jpg">
<img src="boring.jpg">
</div>
But instead of 4 images, you have 100, or 200. You want to bind a click event to images so that X action is performed when the user clicks on it. Most people's first code might look like this:
$('#container img').click(function() {
performAction(this);
});
This is going to bind a crapload of event handlers that will bog down the performance of your page. With Event Delegation, you can do something like this:
$('#container').click(function(e) {
if($(e.target)[0].nodeName.toUpperCase() == 'IMG') {
performAction(e.target);
}
});
This will only bind 1 event to the actual container, you can then figure out what was clicked by using the event's target property and delegate accordingly. This is still kind of a pain, though, and you can actually get this significant performance improvement without doing all this by using jQuery's live function:
$('#container img').live('click', function() {
performAction(this);
});
Hope this helps.
If by "native liveQuery" you mean live(), then yes, live() is significantly faster than liveQuery(). The latter uses setInterval to periodically query the entire document tree for new elements while the former uses event delegation.
Event delegation wins handsdown. In a nutshell, live() will have one handler on the document per event type registered (eg, click), no matter how many selectors you call live() with.
As for your other question, it sounds like you are binding to each slide's elements and want to know if unbinding and binding again is performant? I would say WRT memory, yes. WRT CPU cycles, no.
To be clear, with the liveQuery() approach CPU will never sleep.
For what it's worth. We just ran some tests on this matter. We created a page with a div containing a number of divs, each of which needed to have an onclick handler display an alert dialog with showing their id.
In one case we used DOM Level 0 event registration and defined the event handler for each directly in the html for each: onclick="_do_click(this);". In the other case, we used DOM level 2 event propagation and defined a single event handler on the containing div.
What we found was, at 100,000 contained divs, there was negligible difference in the load time on FireFox. It took a long time period. In Safari, we found that the DOM level 0 took twice the time off the DOM level 2, but was still four times faster than either FireFox case.
So, yes, it does result in better performance, but it seems like you really have to try to create a noticeable penalty.

Categories