Grauw’s blog

A while ago I did some experiments on creating an XML document by constructing a string versus creating it by using the DOM methods, and I found that the latter was faster than the former. QuirksMode by Peter Paul Koch has a Javascript benchmark for creating HTML however, which claims that using innerHTML is 35 times as fast compared to using the DOM (although I can’t reproduce that number in Internet Explorer 8 (IE 7 mode), it is ‘only’ 22 times faster here).

People keep mentioning performance as an argument for using innerHTML, citing that article, so I thought I’d go and see what the reason was for the difference between his and my findings. It turns out that the benchmark has some flaws that skew the results very much in favour of innerHTML, which I’ll point out.

What the benchmark does is, it creates a 50×50 table with a ‘*’ in each cell using different approaches. Basically, there are two tests of importance here, which are:

  1. String construction (using arrays and array.join('')) and parsing the result with innerHTML
  2. Construction using the DOM methods such as document.createElement('div')

1. Content is never escaped

First of all, QuirksMode’s innerHTML tests append strings as follows:

string.push('<td>*</td>');

However, realistically the value of a cell will not be a fixed value, but the value of some variable, which also needs to be escaped to prevent parsing errors. This leads to the following modification:

string.push('<td>');
string.push(escapeHTML('*'));
string.push('</td>');

The escapeHTML function looks like this:

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

This change makes the strings + innerHTML version roughly twice as slow, but more representative of how it is actually used. The DOM methods already do this implicitly, so there is no change in performance there.

2. Selective HTML usage

Secondly, the QuirksMode test makes use of HTML table elements. This also skews the test results a lot, because when changing them to divs, the test performs 4 times better in IE! This is what the changed code looks like:

var x = oDocument.createElement('div');
    var y = x.appendChild(oDocument.createElement('div'));
    for (var i=0;i<50;i++)
    {
        var z = y.appendChild(oDocument.createElement('div'));
        for (var j=0;j<50;j++)
        {
            var a = z.appendChild(oDocument.createElement('div'));

I wonder if there is some workaround or flag you can set to make it perform more like IE 5.5 in this regard (which is much faster, according to QuirksMode).

Also note that this test only tests element creation — in practise you will also be setting attributes, and when expanding the test with tests for adding attributes using string construction versus the (simple) setAttribute() method, the relative performance of the DOM version might show further improvements (I didn’t test this though).

3. XML is much faster than HTML

Finally, the HTML DOM is much, much slower than the XML DOM. When using the XML DOM, it turns out to be faster than constructing a string. The following creates and serializes a document in a cross-browser way:

if (document.all) {
    var oDocument = new ActiveXObject('Microsoft.XMLDOM');
    oDocument.async = false;
    oDocument.preserveWhiteSpace = true;
    oDocument.validateOnParse = false;
    oDocument.resolveExternals = false;
    oDocument.loadXML('<root/>');
} else {
    var oDocument = new DOMParser().parseFromString('<root/>', 'application/xml');
}
if (document.all) {
    document.getElementById('writeroot').innerHTML = oDocument.xml;
} else {
    document.getElementById('writeroot').innerHTML = new XMLSerializer().serializeToString(oDocument);
}

So, to summarize, when using XML there is really no excuse for constructing a string instead of using the DOM. When using HTML, constructing a string is indeed faster, however innerHTML is inherently less robust than using DOM operations, as I wrote earlier today, and the difference is not so big as sketched by QuirksMode. Additionally, if your application is really performance-critical, it would be even faster to construct the document in XML, and then serialising/reparsing or transforming it to HTML.

The final test results are:

Internet Explorer 8 (IE 7 mode) results
Method Running time Relative performance
DOM, using table elements (in original test) 978 ms 2274
DOM, using divs 233 ms 542
DOM, using XML 43 ms 100
Strings, using innerHTML, not escaped (in original test) 47 ms 109
Strings, using innerHTML, escaped 63 ms 147
Firefox 3.0 RC1 results
Method Running time Relative performance
DOM, using table elements (in original test) 119 ms 188
DOM, using divs 110 ms 174
DOM, using XML 63 ms 100
Strings, using innerHTML, not escaped (in original test) 32 ms 51
Strings, using innerHTML, escaped 71 ms 113

Note that the results with ‘in original test’ in parenthesis are results from running the original benchmark.

Update: Caching the regular expressions for escaping HTML strings made IE perform a little faster; updated the measurements.

Update: The article (rather, the copy I put on the Backbase BDN) is featured on Ajaxian. Nice!

Grauw

Comments

My studies show by Willem Opperman at 2009-06-11 16:55

In my studies working with js I have found that createElement is in fact very fast.

I dynamically created(content came from MySQL DB) 1600(40*40) 40px*40px div elements.

At first this crashed the browser...

Then I tried using threads which resulted in a 90 second drawing time for the 1600 divs (one at a time), but I could increase the amount of div objects to almost any amount, I went as far as 10,000 (100*100) but this could easily be increased with a relative increase in drawing time.

After that I tried a different approach:
Create all elements with a css rule of display:none; and append
After this traverse through all created elements and apply display:block.
This took the rendering time from 90 secs to 0.4

So the trick is to create everything, and only render it after memory has been allocated to all objects.

Willem Opperman

Re: My studies show by Grauw at 2009-06-12 16:00

Hmm, but in what order did you append the elements to eachother? If you only append any element to its parent once all its children have been appended, it should never get painted until the last moment as the root element gets appended to the main document. So then it should not matter if display is initially set to none or not.

Still, it might be interesting to try my test with table elements using your display:none-trick. Because I suspect what makes it slow is that the browser already does size calculations as they are created.

by Willem Opperman at 2009-06-17 11:44

I had a 1600px div with display:none;

I created 1600 40*40px float:left; divs and appended them to the 1600px wide div(also stored a reference to them in a 3 dimensional array, for various other purposes)

After that I made the 1600px container div display:block;

But that was not a benchmark test, I was developing an application..

Willem Opperman

Re: by Grauw at 2009-06-18 00:56

Right, yeah, in case you append elements directly into the document, to avoid continuous reflows you would have to either detach the parent, give the parent display:none or use a document fragment. Actually, I would recommend giving the latter a try, then you don’t have to mess around with the style.