Does Javascript remove event handlers of deleted DOM elements? - javascript

If I attached a bunch of events to some DOM elements - and then remove them - will the memory used for their event handlers or other attributes still be used?
I ask because I want to know if I will use a bunch of memory if I keep refilling an area of a page with new elements from AJAX requests and binding events to them - only to delete them and repeat the process when a new AJAX result comes in.

Yes, modern browsers (eventually) release the memory used by event handlers in DOM nodes. However, old versions of Internet Explorer don't, so it's always good practice to remove event listeners before removing the nodes from the DOM.
This is a good article for understanding what's going on: http://msdn.microsoft.com/en-us/library/bb250448(v=vs.85).aspx

Related

Do I need to cleanup my event handlers when DOM changes?

If I make ajax requests that remove the body HTML and append new HTML, do I need to also remove any event handlers that were added to the previous HTML?
I noticed that if I don't, everything works fine. Does the browser free the memory and stuff? What if I do thousands of such ajax requests without refreshing the browser? Will I get memory leaks?
If you add jQuery event handlers :
they will be cleaned if you use jQuery removing/replacing functions
they won't be cleaned if you use direct DOM functions
On most function documentations, you have a comment similar to this one :
When .html() is used to set an element's content, any content that was
in that element is completely replaced by the new content.
Additionally, jQuery removes other constructs such as data and event
handlers from child elements before replacing those elements with the
new content.
If you're coherent, you'll have no memory leak, you don't have to manually remove data or event handlers. There's usually no problem in having a page kept open for days and issuing thousands of Ajax requests and changing the screen accordingly.

How to break all reference loops between DOM and JavaScript in IE8 to allow memory be reclaimed?

I'm trying to get rid of memory leak in IE8, caused by reference loops between event handlers and DOM elements during iframe navigation. I can not modify other scripts on the page.
So the idea was to walk through the DOM and window object and nullify all fields to ensure no DOM element references event handler.
Now the problem is I need to do it after all other unload handlers are run, because other handlers might depend on fields I'm going to nullify.
I tried to do store document object before navigation somewhere in parent window, and then, after navigation in my iframe completes (onload event), run cleanup on stored document object. However, apparently, you can't do it, because after old page is unloaded accessing this document becomes illegal (access error).
The other approach I tried was to find way to add window unload handler, which will be guaranteed to be called the last, however, I did not successed in it so far. To achieve that I tried to call all handlers for the unload event, clean them, and than run my code, but I did not found a way to trigger unload event manually.
Any ideas? Unfortunately, page uses jQuery and Microsoft Ajax, which have their own unload handlers. In particular, my nullification breaks MS Ajax unload handler, because it removes all library namespaces.
I think it's impossible to guarantee the removal of all event listeners if you don't know how they were attached in the first place.
IE 8 uses attachEvent, so if a listener is added like:
element.attachEvent('onclick', function(){...});
then you have no way of knowing that the listener is attached. Setting the onclick property to some new value (say '') will not remove the listener, nor will adding another by attachEvent. In DOM compliant browsers, replacing an element with a clone of itself will remove listeners, but not in IE where attachEVent has been used.
Strategies for avoiding memory leaks usually involve not creating circular references involving DOM elements.
Strategies for removing all listeners generally rely on keeping a list of what's been added, then explicitly removing them before the page unloads.
Both can be difficult and are impossible to guarantee if you don't have control of all code in the page.

Jquery Event Performance vs Memory Usage

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.

Does a javascript event handler exist in memory even after the DOM element to which it is bound is removed?

Given the following code
<div id="app">
<div id="foo" />
</div>
<script>
$('#foo').bind('click', function(){});
</script>
I plan on replacing the contents of #app [e.g. $('#app').html('...');, or innerHTML = '...';]. I know that I can use jQuery's .remove() which calls a 'destroy' handler that unbinds events. The fact that there is a destroy handler set up to remove events leads me to believe that without unbinding the events, when the DOM element is removed, the handler will still exist in memory.
So, if the DOM element #foo no longer exists, does the handler disappear as well, or does it get lost in browser memory?
jQuery keeps track of event handlers itself, which is part of why you need to use unbind (nowadays it's off) if you're removing the element from the DOM not through a jQuery method (if you use jQuery's empty or remove, as you mentioned it handles this itself inernally). This is so jQuery knows it can release its reference to the handler.
If it weren't for that, then in theory, you wouldn't have to do anything because once the DOM element is removed from memory, it's no longer reachable, and so in theory shouldn't keep the event handler in memory. That's the theory. The reality is very different, it can be very easy to end up with a situation (particularly on IE) where neither the DOM element nor the event handler can get cleaned up because they're each causing the other to stick around — a memory leak. JavaScript has no issue with circular references (it understands them and can release two things that are pointing to each other as long as nothing else is pointing to them), but the DOM part of the browser is likely to be written in a different language with a different garbage collection mechanism (IE uses COM, which uses reference counting rather than reachability). jQuery helps you avoid this pitfall with IE (part of why it keeps track of event handlers), but you have to use unbind (nowadays off) (or remove elements via empty, remove, etc.) as a consequence.
The take away message: As you hook, so shall you unhook. :-) (And/or use jQuery when removing elements, since it will handle this.)
Somewhat related: If you're adding and removing elements a lot, you might look to see if event delegation (which jQuery makes really easy via the delegation signatures for on) might help.
Just happened to read the docs on jQuery's empty() method:
To avoid memory leaks, jQuery removes
other constructs such as data and
event handlers from the child elements
before removing the elements
themselves.

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