Monday, August 22, 2011

efficient javascript dom access & dom manipulation

DOM access and DOM manipulation are the most common tasks in browser javascript. And naturally, they are also the most common bottleneck when it comes to javascript performance.

Generally speaking, DOM access and manipulation is expensive, because DOM is not a  part of javascript engine. It is separated and doesn't belong to 'core javascript'.  For browsers, it makes sense because a javascript application probably doesn't need and DOM at all while other languages may need to work with DOM document.

Since DOM access and manipulation is expensive, the guideline is: we should try to reduce DOM access & manipulation to minimum. Let's check some code examples to learn and understand this guideline:

<script>
document.onmousedown = function(){
    var i, LIMIT = 200, end=0, start = new Date().getTime();
    for (i = 0; i < LIMIT; i++) {
        document.getElementById("content").innerHTML += "<p>" + Math.random() + "</p>";
    }
    end = new Date().getTime();
    console.log(end - start);
}
</script>

<div id='content'>
<p>Original Content</p>
</div>

It works, but not efficient. We access DOM elements in a loop. On my computer, the result shows clicking the mouse first time takes more than 2500 ms, which is 2 to 3 seconds.  Let's make it better:

<script>
document.onmousedown = function(){
    var i, LIMIT = 200, end=0, html='', start = new Date().getTime();
    for (i = 0; i < LIMIT; i++) {
        html += "<p>" + Math.random() + "</p>";
    }
    document.getElementById("content").innerHTML += html;
    end = new Date().getTime();
    console.log(end - start);
}
</script>

<div id='content'>
<p>Original Content</p>
</div>

We use a local variable to save the html content first, and this is pure core javascript operations without DOM involved. We avoid DOM access in a loop. On my computer, clicking the mouse first time only takes less than 40 ms, less than 1 second.

But we still have more room to improve. Note that we are using document.getElementById("content").innerHTML += html? Since we are already accessing DOM, using DOM's native appendChild() method is more efficient than using +=. But innerHTML doesn't support appendChild (innerHTML is not in DOM standard, but most browsers support innerHTML feature). So, let's change our code:

<script>
document.onmousedown = function(){
    var i, LIMIT = 200, end=0, html='', start = new Date().getTime();
    for (i = 0; i < LIMIT; i++) {
        html += "<p>" + Math.random() + "</p>";
    }
    var temp = document.createElement("div");
    temp.innerHTML = html;
    document.getElementById("content").appendChild(temp);
    end = new Date().getTime();
    console.log(end - start);
}
</script>

<div id='content'>
<p>Original Content</p>
</div>

This time, clicking the mouse first time only takes less than 10 ms on my computer. So first of all, avoid DOM access in a loop. If we already get a DOM element, try to use its native method.

Second, also quite obvious, save a DOM reference in a local variable and work on the variable. This is quite easy to understand:

Instead of doing
document.getElementById("content").appendChild(child1);
document.getElementById("content").appendChild(child2);
document.getElementById("content").appendChild(child3);

We should do
var e = document.getElementById("content");
e.appendChild(child1);
e.appendChild(child2);
e.appendChild(child3);

No comments: