page speed optimization: writing to DOM using javascript vs. html
I have a question concerning page speed and code optimization. I hav开发者_如何学运维e a page which is populated almost 100% through AJAX calls. My question is: is it faster for me to code several empty divs, spans, whatever into the HTML of the page, then fill those elements using javascript? Or, is it faster to create these elements in javascript and insert and append them? I'm not sure if there IS a big difference either. So, any help/advice in this area would be greatly appreciated.
A couple of years back, I did an experiment on this. It's much faster to assign to the innerHTML
property of an element to create a complex structure than it is to use repeated createElement
appendChild
insertBefore
etc. calls. I've dug up the post I did about it (to the Prototype & script.aculo.us mailing list); below.
Remember that parsing HTML and rendering it quickly is what browsers do, and they're highly optimized to do it. If you assign a string with a complex HTML structure in it to a container element's innerHTML
property, you're making one trip from the JavaScript layer to the browser's rendering layer, after which the browser's parsing and rendering code can proceed uninterrupted.
In contrast, if you're building that some complex structure using the DOM API, not only is there a lot of cross-layer travel happening (JavaScript -> browser -> JavaScript), but the browser is also having to work with the DOM API rather than its internal structures.
Consequently, it's usually worth looking at a well-written JavaScript templating engine (if you want to do this client-side). These will usually "compile" the template once into an easily processed form, and during processing for a particular data set, they'll use tricks like building up the string as fragments in an array via Array#push
, and then getting the final result via Array#join
passing in ""
as the separator. For large strings, that can be faster than string concatenation, although whether it is (and to what degree) is very implementation dependent (Firefox's SpiderMonkey vs. Chrome's V8 vs. IE's JScript), unlike the innerHTML
vs. DOM thing, which only varies in how much faster it is.
Here's the mailing list message from a couple of years back I was talking about (saying basically what I say above; wow, it was two years ago), here's the Pastie it refers to, here's that copied to JSBin, and finally...here's the code: (Note that the code is not intended to be a thing of beauty and a joy forever, it was a quick hack... Still though, yeesh, I'd like to think I'd hack up something a bit better now, two years later.)
It may be worth converting this into something that will work on jsPerf. No time to do that now, I'm afraid.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<style>
#log {
border-bottom: 1px solid black;
}
#log p {
margin: 0;
padding: 0;
}
</style>
<script type='text/javascript' src='//ajax.googleapis.com/ajax/libs/prototype/1/prototype.js'></script>
<script type='text/javascript'>
document.observe('dom:loaded', function() {
$('btnDOMDirect').observe('click', useDOMDirect);
$('btnPrototypeDOM').observe('click', usePrototypeDOM);
$('btnHTML').observe('click', useHTML);
});
var numRows = 10;
var numCols = 10;
function usePrototypeDOM(evt)
{
var table;
var tbody;
var tr;
var td;
var row;
var col;
var start;
var end;
start = (new Date()).getTime();
table = new Element('table');
tbody = new Element('tbody');
table.appendChild(tbody);
for (row = 0; row < numRows; ++row) {
tr = new Element('tr');
tbody.appendChild(tr);
for (col = 0; col < numCols; ++col) {
td = new Element('td');
td.update('Row ' + row + ', col ' + col);
tr.appendChild(td);
}
}
$('targetTable').update(table);
end = (new Date()).getTime();
log('DOM took ' + (end - start) + 'ms');
}
function useDOMDirect(evt)
{
var table;
var tbody;
var tr;
var td;
var row;
var col;
var start;
var end;
if (Prototype.Browser.IE) {
alert("DOM direct doesn't work on IE because I used table elements. Sorry. The other two work.");
return;
}
start = (new Date()).getTime();
table = document.createElement('table');
tbody = document.createElement('tbody');
table.appendChild(tbody);
for (row = 0; row < numRows; ++row) {
tr = document.createElement('tr');
tbody.appendChild(tr);
for (col = 0; col < numCols; ++col) {
td = document.createElement('td');
td.update('Row ' + row + ', col ' + col);
tr.appendChild(td);
}
}
$('targetTable').update(table);
end = (new Date()).getTime();
log('DOM took ' + (end - start) + 'ms');
}
function useHTML(evt)
{
var html;
var row;
var col;
var start;
var end;
start = (new Date()).getTime();
html = '<table><tbody>';
for (row = 0; row < numRows; ++row) {
html += '<tr>';
for (col = 0; col < numCols; ++col) {
html += '<td>Row ' + row + ', col ' + col + '</td>';
}
html += '</tr>';
}
html += '</tbody></table>';
$('targetTable').update(html);
end = (new Date()).getTime();
log('HTML took ' + (end - start) + 'ms');
}
function log(msg)
{
var l;
var p;
l = $('log');
if (l) {
p = new Element('p');
p.update(msg);
l.appendChild(p);
}
}
</script>
</head>
<body>
<input type='button' id='btnDOMDirect' value='DOM Direct' />
<input type='button' id='btnPrototypeDOM' value='Prototype DOM' />
<input type='button' id='btnHTML' value='HTML' />
<div id='log'></div>
<div id='targetTable'></div>
</body>
</html>
It will always be slower using javascript to do this because it runs on top of the page load, rather than with it, as adding elements to the HTML would. However, you could also say that the actual load of the page is less (although not significantly) without having the elements in HTML.
The other point is, though, javascript is pretty bad at garbage collection so if you're making loads of DOM calls it will eventually start to add up in your processing power.
Plus there is also if you're interested in maintaining a semantic website, are you tags necessary? Does it degrade gracefully without javascript? Etc etc. It depends on the angle you are wanting to take I suppose.
If you're creating lots of elements innerHTML can be much faster, however it's not a part of the official DOM standard (though it is widely supported). My recommendation would be to serve the page with a skeleton layout, including as much HTML as you can in the page itself, and then grab references to relevant parts of the page and plug in the values with standard DOM methods.
This should be reasonably fast, will keep the presentation and logic separate, and will probably end up being more flexible in the case of future site changes or redesigns.
Modify innerHTML
instead of using DOM methods.
According to this benchmark of W3C DOM vs innerHTML on Quirksmode, it looks like all tested browsers are much faster at HTML than DOM.
精彩评论