innerHTML versus the DOM: Can’t we all just get along?
19 April 2008 · Estimated reading time: 6 minutes
Having tested the relative speed of innerHTML versus DOM node replacement myself, I read with interest When innerHTML isn’t Fast Enough. I have put together a test of my previous 2 techniques plus one inspired by that article.
Each of the buttons below writes the phrase Chubby bunny 10,000 times to the container div, clears the content, then does it again. What I’m trying to test here is the effect of the different ways of clearing out the DOM nodes from the container. Code and discussion follows.
innerHTML
This technique is the simplest and, probably, the most common on the web today. The following code builds up a long string of HTML, dumps it into the innerHTML
attribute of the container div
, then sets the content back to an empty string. Rinse, and repeat.
// Start 1st pass innerHTML = []; iterations = iters + 1; while (—iterations > 0) { innerHTML[innerHTML.length] = htmlDiv; } resultDiv.innerHTML = innerHTML.join(''); resultDiv.innerHTML = '';// Start 2nd pass innerHTML = []; iterations = iters + 1; while (—iterations > 0) { innerHTML[innerHTML.length] = htmlDiv; } resultDiv.innerHTML = innerHTML.join('');
DOM replace
The following code builds up a document fragment by appending div
nodes to it. The fragment is then appended to the container, a new fragment is built up, then the nodes are swapped out in one step.
// Start 1st pass container = document.createDocumentFragment(); iterations = iters; container.appendChild(nodeDiv); while (—iterations > 0) { container.appendChild(container.firstChild.cloneNode(true)); } resultDiv.appendChild(container);// Start 2nd pass cloneDiv = resultDiv.cloneNode(false); cloneDiv.id = 'result-domreplace'; container = document.createDocumentFragment(); iterations = iters; container.appendChild(nodeDiv); while (—iterations > 0) { container.appendChild(container.firstChild.cloneNode(true)); } resultDiv.parentNode.replaceChild(cloneDiv, resultDiv); cloneDiv.appendChild(container);
innerHTML + DOM replace
The following code combines inserting a string of HTML via the innerHTML
attribute with the one-step swapping goodness of the DOM replace code.
// Start 1st pass innerHTML = []; iterations = iters + 1; while (—iterations > 0) { innerHTML[innerHTML.length] = htmlDiv; } resultDiv.innerHTML = innerHTML.join('');// Start 2nd pass container = resultDiv.cloneNode(false); innerHTML = []; iterations = iters + 1; while (—iterations > 0) { innerHTML[innerHTML.length] = htmlDiv; } container.innerHTML = innerHTML.join(''); resultDiv.parentNode.replaceChild(container, resultDiv);
In theory, the last technique should be the fastest because the bottleneck with innerHTML is in removing the nodes by setting its value to an empty string. By building up a document fragment then swapping it out with the parentNode.replaceChild
technique, we avoid using innerHTML to tear down the DOM.
In my testing, however, results were all over the shop. In Firefox, the results were as I expected: innerHTML was slowest, DOM replacement faster, and the combo technique fastest. In Internet Explorer, innerHTML was the fastest, the combo technique nearly as fast, and straight DOM manipulation nearly an order of magnitude slower. In Safari 3, I found no significant difference among the 3 techniques.
The following numbers are averages of 5 runs of each technique in the various browsers, expressed in milliseconds.
innerHTML | DOM replacement | innerHTML + DOM | |
---|---|---|---|
Safari 3 | 109 | 104 | 96 |
Safari 2 | 489 | 849 | 457 |
Firefox 3b4 | 1588 | 1030 | 523 |
Firefox 2 | 1386 | 1019 | 401 |
IE 8b1 | 900 | 2131 | 844 |
IE 7 | 290 | 1275 | 475 |
IE 6 | 339 | 1880 | 373 |
Opera 9.27 | 266 | 847 | 675 |
From the above, it looks like the biggest advantage of the combo technique is its consistency across browsers. While straight innerHTML is faster on some browsers and straight DOM manipulation is faster on others, combining the 2 techniques yields decent performance across browsers.
Update: I added numbers for Safari 2, IE 8b1 and Opera 9.27 to the list above. My conclusion still appears to hold.