views:

526

answers:

4

I use this method to escape strings of HTML code on client side:

function escapeHTML(str)
{
   var div = document.createElement('div');
   var text = document.createTextNode(str);
   div.appendChild(text);
   return div.innerHTML;
};

I am a bit concerned about memory leaks because I'm creating DOM element and not destroying it. The page is heavily Ajax'ed and will be refreshed very rarely, so there might be hundreds of calls to the method without page reload, which means those objects will be piling up, slowing down DOM traveral (e.g. with jQuery)

I tried to use document.removeChild(div), but IE gives an error "htmlfile: Invalid argument."

Any ideas?

Thanks, Andrey

A: 

IIRC you shouldn't have to care, since JS is garbage collected. If it's a huge deal, you could try parsing in chunks called via setTimeout() with a very short interval.

Wevah
I should add that since your div is never added to the document element, it's never part of the document's DOM.
Wevah
+1  A: 

If you have jQuery loaded, you could use the remove method.

Mark
+1  A: 

DOM:

var node = document.getElementById('node_to_delete');
node.parentNode.removeChild(node);

jQuery:

$('#node_to_delete').remove();
Anatoliy
+14  A: 

You need to call removeChild on a an element itself:

function escapeHTML(str) {
   var div = document.createElement('div');
   var text = document.createTextNode(str);
   div.appendChild(text);
   var escapedHTML = div.innerHTML;
   div.removeChild(text);
   return escapedHTML;
}

One thing to remember is that this solution has quirks in some of the browsers (such as handling of newlines = "\n"). In Prototype.js, we explicitly check for some of these quirks and tweak the logic appropriately.

And of course it goes without saying that you should use feature detection, not browser sniffing ;)

You also don't really need to create new element every time function is called. Simply store it in a closure. For example:

var escapeHTML = (function(){

  var div = document.createElement('div');
  var text = document.createTextNode('');

  div.appendChild(text);

  return function(str) {
    text.data = str;
    return div.innerHTML;
  };
})();

If you employ this approach and keep element permanently, you might also consider cleaning it (i.e. nulling) on page unload, to prevent possible leaks.

Unfortunately, merely registering unload event handler prevents fast navigation (aka page cache) in many browsers. Most of the JS libraries already observe that event for cleanup purposes, so if you're using one (e.g. Prototype.js, jQuery, YUI), you shouldn't worry about it - page cache will be disabled anyway.

Otherwise, you might want to reconsider your options and either always create element at runtime, or employ different solution altogether (e.g. String.prototype.replace -based one):

function escapeHTML(str) {
  return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
}

Oh, and finally, you don't need to place ";" after function declarations; it is function expressions that are recommended to be ended with semicolons :)

kangax
One of the best answers I've seen on SO in a long time!
J-P
Above and beyond. Nice answer.
Crescent Fresh
Thanks guys :)
kangax
Good answer. Re last paragraph: ironcially, you've missed off a semi-colon after one of your function expressions :)
Tim Down
@Tim Down Ha! Yes, ironic indeed. Will fix. This is why code reviews are important :)
kangax
I'm sure this is me not understanding the DOM properly, but I don't understand your first example. Two objects (an element and a textnode) are created, but only one of them is deleted. Surely a problem remains, if one ever existed. However, DOES a problem even exist in the first place? An object is being created, then assigned to a local variable. When the function exits, and the local variable goes out of scope, doesn't that object get garbage collected? It's been a long time since I even gave passing thought to issues of stack and heap, so please forgive me for being a little rusty!
Bobby Jack
@Bobby, in Javascript, objects get garbage collected as soon as there no longer exist any references to them (and as soon as garbage collection process runs). The problem with storing DOM objects in a closure — as seen in a second example — technically should not exist, since there's no circular reference between host and native object — a pattern that triggers leak in IE. However, I clearly remember reports in Prototype.js bug tracker about leaks with this particular snippet. IIRC, the symptom was that memory use was not rising, but it wasn't decreasing either...
kangax