Should I call jQuery.off after removing the DOM element? - javascript

I am registering a click listener on a DOM element via jQuery.on(). If later on that element is removed from the DOM -- maybe indirectly, e.g. by by replacing some parent's content via $(parent).html(...), should I still bother to remove my handler via jQuery.off()?
Even if the element will no longer trigger any event, I am worried about potential memory leaks. Does either jQuery or the browser take care of that and discard all registered handlers once an element is removed from the DOM?

Even if the element will no longer trigger any event, I am worried about potential memory leaks.
This is very good concern. To answer your question, take a look at $.fn.html implementation. From there you will learn that html will try to clean up stored event data:
// Remove element nodes and prevent memory leaks
if (elem.nodeType === 1) {
jQuery.cleanData(getAll(elem, false));
elem.innerHTML = value;
}
So in this case manually calling .off() is not necessary. However..
You need to remember that you should never ever try to remove elements with native methods like removeChild or setting innerHTML, since in this case there will be a memory leak for sure (if some data is stored, events are registered by jQuery, etc.). In this case it's more reliable to actually deregister event handlers with .off method. Or better use event propagation and instead or html('') use $.fn.remove.

It's better to call jQuery.off before removing your node, especially if it's a one page application which can contains a lot of events registered.

Related

Should I attach my .on('click') event to the document or element

Yesterday I was reading the jQuery docs for .on() where was stated:
Avoid excessive use of document or document.body for delegated events on large documents
But today, I was looking at this JSPERF and I notice a better performance when the click event is attached to the document.
So right now, I'm confused. The performance tests speak against the docs?
Your JSPerf here is testing the speed to attach events, not the effect that they have on cumulative page performance. This is the wrong thing to test!
Javascript events propagate up the DOM all the way to the document root. This means that if you have an on("click", ...) handler on document, then every click on every element in the document will end up running an event handler, so jQuery can test if its origin matches the delegate target, to see if it should be passed to that event handler.
Imagine that your page has 10 different delegated event handlers on document, all handling various clicks. Every time you click any element in the page, the event will bubble up to the document root, and all 10 of those handlers have to be tested to figure out which (if any) should be run.
In general, you want your delegated events to be as deep in the tree as possible while still enabling your functionality, since this limits the number of elements that may invoke this event, and you can handle the event earlier to prevent it from propagating up the DOM tree.
It depends.
You can attach handler to any element you want, of course, and in some cases you will have to attach it to document or body (if you, for example, want to target all the links on the page). But, if you are sure that certain elements will always appear only inside given element (which is already created) - then for performance sake, you can attach event handler to that common parent.
The point is excessive.
IMHO excessive delegates on any DOM is terrible

Is it bad to bind behaviours to document unconditionally?

The pagination controls on a page I am working on were being bound conditionally on there being more than 1 page. I don't like to see the following code in my projects,
if (pages > 1) {
$('.some_class').bind('event', function() {});
}
because I feel it represents a disorganized coding style. I would put it on the same level as sprinkling return statements here and there rather than using control. I feel like binding events to globally available objects has no place in the local scope of a function call. So what I usually do is make two javascript files, for example: pagination.js and pagination-controls.js. In the one I have logic about building the html and displaying the the pagination controls. In the other I have statements like the following:
$(document).on('click', '.pagination .next', function() {});
Which fires regardless of whether there is a $('.pagination .next') element anywhere on the page. I like the way that feels: the website has behaviours and it only knows about ids and classes, not about instance variables in some local scope somewhere.
EDIT: this is definitely bad practice, as mentioned below. However:
As of jQuery 1.7, the .on() method is the preferred method for
attaching event handlers to a document.
and the discussion on direct and delegated events is relevant. In particular I think the following describes my usage:
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.
EDIT: So I guess now I'm wondering "is it bad to prefer binding behaviours to parent elements unconditionally over binding based on logic?" That's perhaps just a question of style, and my original question has been answered so I think I will accept the answer.
Yes, this is causing significant unnecessary overhead, and it is a "bad practice".
Binding your event handling to the top-level document object means that every single click that occurs on any element anywhere in your page will bubble up to the document object, where the event's target is checked to see if it matches .pagination .next.
In fact, the documentation itself recommends against your usage:
Attaching many delegated event handlers near the top of the document tree can degrade performance. Each time the event occurs, jQuery must compare all selectors of all attached events of that type to every element in the path from the event target up to the top of the document. For best performance, attach delegated events at a document location as close as possible to the target elements. Avoid excessive use of document or document.body for delegated events on large documents.
So, you're misusing on. It's for binding directly to elements or to parent elements which may have dynamically created children, and you are meant to bind to the closest possible parent element. Binding to the document is certainly not meant to be the only way you handle events in your page.

Should I remove events before changing a div content?

I've inherited a jQuery application that does its work by calling AJAX services without leaving the page.
There is a list of items, where when you click on an item the detail of the item is displayed, events attached to the buttons, then the html is disposed and new html will be created, new events attached and so on and so forth.
I was wondering whether JavaScript garbage collection will automatically clean up those events, or do they go in some Map like data structure and create a memory leak. Am I supposed to clean them up explicitly?
If you always bind the same events, you could also use something like jQuery live() or the likes instead of always binding/unbinding them.
Javascript garbage collection will not remove bound events to removed elements. jQuery functions often do depending on what method you use.
In example, .html() or .remove() will remove events and not leak memory. When in doubt you can always use .empty().
In my opinion the .live() discussion is fairly beside the point of memory leaks. The usage of .live() should be done when it makes sense from an architecture standpoint. Unless you need the event to be bound before the element is added to the DOM, or exist after the element is removed (because it may be added again) then .bind() is really the proper and faster method to use. .live() is slower than .bind() because each event must fire at the element, bubble to the document and then bubble back up to the element in order to be processed by .live(), while with .bind it can execute right away.
Also, FYI both .bind() and .live() are deprecated in the newest version of jQuery (1.7). They will still function, but the new syntax is .on(), using delegation to replace .live(). This is because too much .live() really slows down pages because each .live binds another event to the document which must be listened and processed every single event.
if you want to make it easier on the GC and not make him search if it has references , so you should unbind / die all events to the div.

Circumvent the DOMNodeInsertedIntoDocument event

Assume I have a javascript function createMyElement which returns a node that can be inserted into an HTML document.
In order to function properly, the code of the node created by createMyElement has to listen for events on the global document at least as soon as it is inserted in the document.
My first attempt was to add DOMNodeInsertedIntoDocument and DOMNodeRemovedFromDocument listeners to the node at creation time that add and remove the needed listener on document in turn.
However, the mutation events are deprecated by now (and don't seem to work reliably across browsers), so I am looking for a better solution.
Adding the listener for events at document at the creation time of the node would work. However, this doesn't seem to be a good solution as it would create memory and performance leaks: Even after the node was being removed from the document again and not needed anymore, the listener (and its references to the node) on the document would still persist.
Use a series of function callbacks or on-demand script callbacks to serialize the events.
Since I originally asked my question, the substitute for mutation events, namely mutation observers as defined in http://www.w3.org/TR/domcore/#mutation-observers, have been implemented in a number of browsers.
So a simple answer to my own question is to simply use mutation observers on the document to listen for nodes to be inserted or removed.
An even better way, however, is to use the new custom elements from https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html, which gives one insertedCallbacks and removedCallbacks for custom elements.

JQuery cleaning up eventhandlers

I'm an actionscript developer getting into jquery/javascript development. I have a question regarding event handlers and binding/unbinding.
Say for instance that I have for an div with a img element with an onerror event handler in it. If i replace that that div with a new one do i need to remove the eventhandler bound to the img element. Since the img no longer will be in the document will browsers be smart enough to remove it or will I have a caused memory leak?
Comming from actionscript i usually try to constantly remove old eventhandlers. So do i need to do this when writing javascript for web browsers?
The event handlers are added with $('imgElement').error(errorFunction);
If you're binding the events with jQuery just call .remove() on the old element before replacing it, or .empty() if you just want to clear it, both of these clean up event handlers for the element and it's children, or in the case of .empty(), just the children.
If you just replace it, e.g. .html(content) you will leak memory, as any handlers or data for those elements will be left on the $.cache object.
try using firequery, its a firefox plugin that shows all active event listeners and jquery.data()
I'm also curious as to whether it's best practice to unbind() events...

Categories

Resources