Performance Tip #2: Iterating Over Node Collections

It’s fairly common in JavaScript to get a list of nodes from a DOM element and then iterate over them to perform some function such as setting a class name. Typically this is done with something like:

 var list = document.getElementById("itemList"); // ul containing 100 li elements
for (var i = 0; i < list.childNodes.length; i++) {
    list.childNodes[i].innerHTML = "New Item #" + i;
}

It looks pretty harmless but you need to consider the length of the nodes is actually a lookup from the DOM and not a simple comparison. As it’s a lookup it’s also can change so for every loop that statement needs to be evaluated. Realistically in JavaScript the length of the collection is very unlikely to change during execution, mainly because JavaScript is single threaded and in in IE it shares the UI thread so only IE or JavaScript can be interacting with that collection (for most intents and purposes). Thus a simple optimization would be to cache the length outside the loop:

 var list = document.getElementById("itemList"); // ul containing 100 li elements
var numberOfNodes = list.childNodes.length;
for (var i = 0; i < numberOfNodes; i++) {
    list.childNodes[i].innerHTML = "Item #" + i;
}

Really this doesn’t gain you much as it’s a small fraction of the time compared to what ever you’re going to do inside the loop. It does mean that if in the future engines unwind loops the code above would benefit as it’s a much harder to unwind a volatile loop. But writing code hoping that in the future engines can take advantage of it is a poor idea and not something worth pursuing. In general it’s probably a bad idea to code patterns for specific engines as it may be slower on others.

There is another way  to iterate over a list using a loop and that’s to walk the node list, itself using a while loop, something like:

 var list = document.getElementById("itemList"); // ul containing 100 li elements
var node = list.firstChild;
var i = 0;
while (node) {
    node.innerHTML = "Item #" + i;
    node = node.nextSibling;
    i++;
}

If you time a 1000 iterations of each above loops you the following times in milliseconds:

  For Loop #1 For Loop #2 While Loop
IE8 4304.4 4142.4 3253.4
Chrome 4.1 394 323.8 139.2
Firefox 3.6 1772.8 1717.8 1642.7

Iterating nodes is probably less common a pattern in JavaScript as you have to be careful around cross browser differences and in making sure you get the right node. But it just so happens that in all major browsers it’s faster. Why? Probably because it’s more similar to how the list is traversed in their internally implementations, but that’s speculation.