Amazon.ca Widgets

High performance JavaScript htmlEncode

I recently had to encode lot of strings to HTML in client-side javascript.  There’s a lot of find-replace procedures we can find all over the web, but I think that the most reliable way to encode string is to use the browser itself.

One very easy method to encode a string, that we find everywhere, is this one: 

function htmlEncode(_s){
  return $("<span />").text(_s).html()
}

If we use that quick jQuery function, we know that the result is totally safe, by allowing the browser to do the job on our behalf.

But, using the DOM is sometimes the cause of a slow application…
Does this method is reliable if we need to convert thousands of strings?

I did the test, by repeating the conversion 100000 times.

The previous function, creates a new “span” on every call, and modify the innerHTML of it.

Chrome console “performance monitor” shows that there are 3 DOM nodes created on each call. It takes an average of 2200 ms to run the loop, and generates lots of garbage collection to remove these old DOM items.

Take 2

So, let’s try to re-use the previous span instead without re-creating it.

var $span;
function htmlEncode(_s){
   $span = $span || $('<span />');
   return $span.text(_s).html();
}

This time, on each call, 2 DOM nodes instead of 3 are created.
And, the average time goes down to 1600 ms instead of 2200.

Take 3

Then, I tried to see what jQuery does, to see if it’s possible to re-use the inner text objects that are re-created on each call.

Now, try that 100% non-jquery method. (That you can achieve by using jquery and editing the childNodes directlly…)

var spn, txt;
function htmlEncode(_s){
   if (!spn) {
      spn = document.createElement("span");
      txt = document.createTextNode("");
      spn.appendChild(txt);
   }
   txt.data = _s;
   return spn.innerHTML;
}

That final version of our custom htmlEncode function looks very powerful!

Now, 0 nodes are created on each loop (3 on the first call), we always re-use the same 3 nodes.
And, the loop time is now 100 ms, with absolutely no garbage collection!

Summary

Look at these analysis from the Chrome console.

You can try it by yourself on that demo page I used to generate the previous tests.