Jquery Event Performance vs Memory Usage - javascript

I'm working on a completely ajax web project where a section content is always generated through DOM manipulation or using jQuery's load function. I had been using "live" but am very interested in moving away from "live" and using "on" for performance benefits. When a new page loads a whole new set of bindings required for that section also need to get loaded. The html sections have some parent DOMs (basically wrappers for different content areas of the web page) that never change allowing me to do bindings on them for all future DOM elements that will be created on the page.
In terms of memory and performance trade off which is generally the better way to handle event bindings?
After a new section has finished loading its html, bind all the events needed for that specific page instance on DOM elements that will be removed when a page changes.
Bind every event on the very first page load to DOM elements (not to the document though like live does) that are known to always exist.

Memory issues with listeners can usually be dealt with fairly easily (don't hold large chunks of data in closures, don't create circular references, use delegation, etc.).
"Live" just uses delegation (as far as I know) - you can implement delegation quite simply without it using simple criteria, e.g. class or id, with listeners on the unchanging parent elements. Delegation is a good strategy where it replaces numerous other listeners, the content is constantly being changed and identifying elements that should call functions is simple.
If you follow a strategy of attaching numerous new listeners every time content changes, you also have to detach the old ones when they are replaced as a strategy to reduce the likelihood of memory leaks. Performance (in terms of time taken to attach and remove listeners as part of the DOM update) usually isn't that much of an issue unless you are doing hundreds of them.
With delegation, a parent element listens for events, checks if the event.target/srcElement is one it cares about for that event, then calls the appropriate function perhaps using call to set the value of this if required.
Note that you can also simply include inline listeners in the inserted HTML, then you never need to worry about memory leaks, delegation or adding and removing listeners. Inline listeners using a simple function call are no more complex than adding any other attribute (class, id, whatever) and require zero extra programming on the client. I don't think inline listeners were ever an issue for memory leaks.
Of course the "unobtrusive javascript" mob will howl, but they are very practical, functional and robust, not to mention supported by every browser that ever supported javascript.

Related

Why does Event Delegation save memory?

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.

Understanding React's Synthetic Event System

This has been on my mind for a few days now.
As per the docs, React has synthetic event system, which is a a cross-browser wrapper around the browser's native event. Going through the docs, is my understanding correct that the custom (synthetic) event system, isn't about efficiency but rather cross-browser compatibility.
In other words, React still appends the event to the element rather than the more efficient approach of event-delegation on the parent element?
I also noticed this in Firefox Inspector which raised the initial curiosity.
The reason for asking the question is that I am working on an app where a user maybe able to select a thousand elements & drag them around the screen, so eventually event delegation is going to come up.
Alright, you perhaps already figured everything on your own, but as I asked myself the same questions, I figured I'd leave this here in case someone else is curious about not only using React but also getting an idea about how it works.
So, I'm not entirely sure about your question (especially the "append the event to element" part) but:
React is all about the virtual DOM. As the name implies, it is therefore built on top of the "real" environment that is the DOM. Consequently, everything takes place in that abstracted layer, including event handling.
Events appear in their "natural" environment, so the DOM or native (depending on the flavor of react you are using)
Consequently, you first need to bring the events up to the virtual DOM, compute your changes there and dispatch them to the representation of components in the virtual DOM, then bring the relevant changes back down to be reflected in the DOM appropriately.
Carrying changes up to the virtual DOM is effectively done by top-level delegation. This means that React itself listens to all events at a document level. This also means that technically, all your events go through one capture + bubbling loop before even entering the React-specific code. I would not be able to say what that implies performance wise, because you do "lose" the time associated to that first DOM traversal, but on the other hand you will do all your changes in the virtual DOM, which is faster than doing them in the real DOM...
Finally, SyntheticEvent is indeed a wrapper, which aims at reducing cross-browser compatibility issues. It also introduces pooling, which makes the thing faster by reducing garbage collection time. Besides, as one native event can generate several SyntheticEvent, it technically lets you create new ones easily (like a syntheticTap event that could be emitted if you receive a native touchStart then a native touchEnd in succession).
I have written a post with more details here. It is far from perfect and their might be some imprecision, but it can perhaps give you some more info on the topic.

How expensive is listening for events on the entire document?

I want to use event delegation on a number of buttons in a page of HTML. These buttons are all over the page, and I was wondering how expensive it would be to listen to the entire document for on click events, and then just have those on click events trigger an event delegation handler. Would this be more expensive than having listeners on each of 20+ buttons (it can grow to be over 100 buttons, yes it is silly)?
I don't see how it would be more expensive since it would be listening for clicks on the document object instead of 25 anchor objects
The key idea here is depth. Your event has to traverse up the DOM before it's being executed. If your elements are deep down the DOM tree you may notice some performance degradation.
Couple of things to bear in mind:
the number of anchors doesn't matter for event delegation, that is true
generally speaking event delegation is a superior alternative in most cases, but it's not useful all the time
My suggestion is to analyze these kind of problems, learn how things work, and make decisions by good old common-sense.
I don't see how it would be more expensive since it would be listening for clicks on the document object instead of 25 anchor objects. With that said, just 25-30 buttons is not really resource-intensive so you probably don't need to worry about this.
This is the strategy used by, for example, the jQuery "live" method: listen on the whole document then test the sender against a condition (i.e., selector). Unless the selector is unbearably intensive, this technique is more efficient for large and growing sets of targets.
Did you hard? working code in not a good code.
if you use 20~40 button listeners in a page, instead of using delegate then it will work and probably you will not even see any performance issue. I think you will use forin/$.each to bind all those listeners, so you will not need to write codes for each listeners.
so, my request is please use delegate.
If in future you need to change the logic or you decide to test the application then you will be in trouble.

Do DOM events destroy after the element, they are bound to, dies?

If I bind an event to a DOM element, does the event ever get destroyed if the element does? By destroying of an element I'm referring to removeChild(). A move of the node to another location with appendChild() leaves the event listener untouched.
Basically I'm interested in this because I want to know if I need to do some cleanup/tear down.
I think you mean event handler here, right? If so then it is a valid concern for garbage collection purposes to be careful with functions attached to elements via "onfoo" attributes. IE has what amounts to separate garbage collectors for the DOM and for JavaScript, and they don't know much about each other.
I believe what suffices is to make sure that "onfoo" attributes are set to null when DOM elements are tossed aside. By so doing, the JavaScript code will have broken the reference to JavaScript memory allocated for the handlers, so the DOM garbage collector won't leak. Of course this goes for any other random attributes you may have added to DOM elements too.
Though I hate to suggest using a JavaScript framework for questions not so tagged, and in fact I won't actually make such a suggestion here, but I will say that one of the things frameworks (usually) do for you is try to keep the DOM "clean" in this kind of situation.

Do Javascript event listeners need to be removed prior to removing the element they're attached to?

Suppose I have attached a variety of event listeners to various form elements. Later, I want to remove the entire form.
Is it necessary (or suggested) to unregister any event handlers that exist on the form and its elements? If so, what is the easiest way to remove all listeners on a collection of elements? What are the repercussions of not doing so? I'm using Prototype, if it matters.
Here's what I'm actually doing. I have a simple form, like this:
<form id="form">
<input type="text" id="foo"/>
<input type="text" id="bar"/>
</form>
I observe various events on the inputs, e.g.:
$('foo').observe('keypress', onFooKeypress);
$('bar').observe('keypress', onBarKeypress);
etc.
The form is submitted via AJAX and the response is a new copy of the form. I replace the old form with a copy of the new one doing something like $('form').replace(newForm). Am I accumulating a bunch of event cruft?
Events which are not unregistered may not free their memory automatically. This is especially a problem in older versions of IE.
Prototype used to have an automatic garbage collection system for this, but the method has been removed in version 1.6. It is documented here. Whether the removal of the method means that the garbage collection no longer takes place, or the method just isn't publicly available anymore, I don't know. Also note that it was only ever called on page unload, meaning that if your users stay on the same page for a long time while doing a lot of AJAX and DOM updates, memory may leak to an unacceptable extent even during that one page visit.
Yeah, a bit. Not enough to be a huge problem, but older versions of IE will leak under those circumstances.
As of Prototype 1.6.1 (currently in its final release candidate), the library handles this cleanup on page unload. When you use Prototype to add an event observer, it keeps a reference to that element in an array; on page unload, it loops through that array and removes all your observers.
However, if the user is going to stay on this page for a while, memory usage will accumulate over the life of the page. You have several options:
Listen for events on an ancestor of the form, one that never gets replaced. Then, in your handler, check where the event came from. (i.e., "event delegation")
Explicitly un-register all your calls before calling Element#replace. In your example, you'd do:
$('foo', 'bar').each(Element.stopObserving);
This is equivalent to calling stopObserving with no arguments, which has the effect of removing all handlers on a given element.
I would recommend option 1.
(We've talked about doing automatic listener removal in a future version of Prototype as part of Element#update and Element#replace, but it's a performance trade-off.)
It's always good to remove any event listeners from elements that are removed from the DOM in case of the scenario that Jonas mentioned below.

Categories