A couple of years ago, I wrote the original version of this page to test the relative speeds of using innerHTML versus native DOM methods for attaching nodes to a web page.
Sine then, Peter-Paul Koch did some testing and found that the proper way to measure actual rendering to the screen is to decouple the function for measuring the end time of the test. In other words, it is necessary to allow the DOM script function to end before measuring the time. Safari (and, presumably other WebKit browsers) offloads the actual rendering of DOM elements to another process, which makes it return deceptively fast times using my previous method.
This page has been updated using PPK's method.
For the record, I think the way Safari treats this operation is actually correct from a user-experience perspective. There is no good reason to cause the UI to be unresponsive while the browser works to attach 1000s of DOM nodes if it doesn't have to be.
My new conclusion? In terms of speed, there is no appreciable difference between innerHTML and native APIs when adding something on the order of 1000 nodes to the DOM. There is, of course, a benefit to using innerHTML in terms of simplicity, but there may be cases where it makes sense to construct DOM nodes manually via the DOM APIs.
I ran the two methods outlined below on my Apple MacBook (2.0GHz Core 2 duo processor, 2GB of RAM) in several browsers. I ran each test on each browser 5 times (well, 6 times, but I disregarded the first run) set to loop 1000 times.
So you know, I wasn’t terribly careful about closing all other apps, rebooting before each test run, yadda, yadda, yadda. Non-scientific as my results are, I still found it a useful way to compare innerHTML
to DOM scripting as well as get a rough idea of the efficiency of the two implementations in various browsers. (Note: Windows tests were run using VMWare 2.0.5, running a freshly rebooted Windows XP Home.)
Results are listed as: average time in milliseconds (min time / max time). Fastest scores for each platform and type are in green. Slowest in red.
Mac OS X | innerHTML | DOM |
---|---|---|
Safari 4 | 55.6 (51 / 73) | 56.6 (55 / 58) |
Opera 10 | 73.2 (71 / 75) | 72.2 (71 / 73) |
Firefox 3 | 231.8 (196 / 265) | 240.0 (209 / 300) |
Google Chrome | 51.4 (49 / 55) | 57.6 (55 / 61) |
Windows XP | innerHTML | DOM |
---|---|---|
Safari 4 | 62.8 (58 / 74) | 60.4 (52 / 67) |
Opera 10 | 53.2 (47 / 63) | 53.4 (47 / 63) |
Firefox 3 | 102.8 (101 / 104) | 106.6 (104 / 111) |
Google Chrome | 65.2 (62 / 68) | 68.4 (67 / 70) |
Internet Explorer 8 | 109.4 (109 / 110) | 153.2 (140 / 187) |
Internet Explorer 7 | 74.8 (62 / 78) | 140.8 (140 / 141) |
Internet Explorer 6 | 46.8 (31 / 62) | 118.6 (109 / 125) |
Could some Windows users please verify that my results are not way off? It would turn my head inside-out to have IE6 be 2x as fast as IE8 for innerHTML!
Just for kicks, I tried this on my iPhone. There is clearly a lesson to be learned here for developers of iPhone web applications: keep DOM manipulations of any stripe to a minimum!
iPhone OS 3.1.2 (3G) | innerHTML | DOM |
---|---|---|
Mobile Safari | 3841.8 (3668 / 4065) | 4229.4 (4005 / 4459) |
Loop times
var html; html = []; ++iters; while (--iters > 0) { html[html.length] = content.string; } document.getElementById('innerhtml').innerHTML = html.join('');
var frag; frag = document.createDocumentFragment(); ++iters; while (--iters > 0) { frag.appendChild(content.dom.cloneNode(true)); } document.getElementById('domnodes').appendChild(frag);