More JavaScript Event Listener Memory Leak Prevention

In a previous article, JavaScript Built-in Listeners and Memory Leaks, I described one method of wrapping built-in JavaScript DOM events into a custom event handler so listeners can be properly cleaned up to prevent memory leaks. In this article I'll describe a use of removeBuiltinListener to save you a step if you're adding and removing a lot of DOM elements which have listeners attached.

If you remember, I explained that the reason browsers leak memory when dealing with JavaScript applications is because of the separate garbage collectors in JavaScript and the DOM engines. The way to prevent these leaks is to break the link between the two engines by removing the attached listeners.

When adding and removing a large number of DOM elements to your application it can become tedious to have to keep track and remove all the event listeners attached to an object. To make that particular task easier I made removeBuiltinListener so you can provide just the DOM element as the first parameter and it will remove all listeners for that element. However, what if you want to delete a DOM element that has event listeners attached and also contains child elements which have event listeners attached? Just using removeBuiltinListener would require you to know every child element that has event listeners attached and removing them from each of those elements. Enter DeleteNode and DeleteChildren.

DeleteNode and DeleteChildren

I'll start off by explaining that DeleteNode and DeleteChildren are within the global namespace. I have not developed a framework (yet?) and don't use any others so I don't have to be concerned with collision of names, but just keep the issue in mind if you use these.

Just as their names imply, DeleteNode deletes a DOM node element and DeleteChildren deletes a DOM node element's children. Instead of using node.parentNode.removeChild(node) for a single element or node.removeChild(childNode) for each child element you can call DeleteNode(node) or DeleteChildren(node) which will also remove the listeners attached to each element.

Since these are pretty straight forward and I've included inline comments, I'll just throw the code out without any further explanation except to say this is dependent on my first two articles, Custom JavaScript Event Listeners and JavaScript Built-in Listeners and Memory Leaks:

  1. function DeleteNode(node)
  2. {
  3.         if(node) //make sure the node exists
  4.         {
  5.                 DeleteChildren(node); //delete node's children
  6.                 gEVENTS.removeBuiltinListener(node); //remove listeners
  7.                 if (typeof node.outerHTML !== 'undefined')
  8.                         node.outerHTML = ''; //prevent pseudo-leak in IE
  9.                 else
  10.                         if(node.parentNode) //if the node has a parent
  11.                                 node.parentNode.removeChild(node); //remove the node from the DOM tree
  12.                 delete node; //clean up just to be sure
  13.         }
  14. }
  15.  
  16. function DeleteChildren(node)
  17. {
  18.         if(node) //make sure the node exists
  19.         {
  20.                 for(var x = node.childNodes.length - 1; x >= 0; x--) //delete all of node's children
  21.                 {
  22.                         var childNode = node.childNodes[x];
  23.                         if(childNode.hasChildNodes()) //if the child node has children then delete them first
  24.                                 DeleteChildren(childNode);
  25.                         gEVENTS.removeBuiltinListener(childNode); //remove listeners
  26.                         if (typeof childNode.outerHTML !== 'undefined')
  27.                                 childNode.outerHTML = ''; //prevent pseudo-leak in IE
  28.                         else
  29.                                 node.removeChild(childNode); //remove the child from the DOM tree
  30.                         delete childNode; //clean up just to be sure
  31.                 }
  32.         }
  33. }