Blog Post default image

The DOM

Yep here we are, time to get to the bottom of this spooky beast we call the DOM. If you are not familiar with the Document Object Model, you might need to read up before continuing. But since I assume you know what your doing to be reading this far anyway, here are some cool ways to speed it up, or at least look like you are.

  • HTML Collections
  • Reflow

HTML Collections

I suggest watching the video to find out more about them, but I will explain them very quickly. The following are some examples of HTML collections.

HTML collection examples

{code type=php}
var div = document.getElementsByTagName(“div”);
document.images
document.forms
getElementsByTagName()
getElementsByClassName()
{/code}

Collections in the HTML DOM are assumed to be live, meaning they are automatically updated if the DOM is changed. If your function is adding more divs to the document then the collection length will change. Collections are not arrays, even though they behave in a similar way. Collections in loops are bad because if you coded something like this.

{code type=php}
// slow
var div = document.getElementsByTagName(“div”);
for (var i=0; i < div.length; i++){ } {/code} You are actually calculating the number of divs in the page every time div.length is called. Very very slow. Here is a better way of coding it. {code type=php} // fast var div = document.getElementsByTagName("div"); for (var i=0; len=div.length; i < len; i++){ } {/code} div.length is stored in a local variable called once, hence much faster. Again check out the Google talk video for more information about collections.

Reflow

Reflow, dam important, even if you had never heard of it! As briefly described at the start of this document DOM Reflow is simply put as when the page is forced to redraw itself, more complicated explanations are around but that is the gist of it. Reflow isn’t necessarily a bad thing and without it we couldn’t do cool things like fluid layouts, or moveable objects. But are you causing your page to reflow more then you need to? Are you forcing the browser to redraw/reflow when there isn’t a need to and if so how do you stop it and why.

The reasons why you don’t want to cause needless Reflow are simple, it takes time to compute the new geometry of the page. That time can of course be perceived by a user as a slow down, or overall slow mechanics of your web site, leading to negative opinions about your sites speed.

Here are some things that cause Reflow in a typical web site.

  • Initial page load (un-avoidable)
  • Browser resizing (un-avoidable)
  • DOM node are added or removed
  • Layout styles applied

Obviously we can do something about the last three there.

Adding DOM nodes

Here is an example of how to cause lots of Reflow in your page, and is a pretty common scenario that I see all the time in peoples code.

Typical way of appending new DOM nodes

{code type=php}
// slow
var list = document.getElementById(“list”);
for( var i=0; i < 10; i++ ) { var item = document.createElement("li"); item.innerHTML = "option #" + (i+1); list.appendChild(item); // Reflow 10 times } {/code} Yep all those nodes getting appended cause a Reflow each time, so here is a much better way of accomplishing the same thing using an amazing little thing called a "document fragment".

Far better way of appending new DOM nodes

{code type=php}
// fast
var list = document.getElementById(“list”);
var document = createDocumentFragment();

for( var i=0; i < 10; i++ ) { var item = document.createElement("li"); item.innerHTML = "option #" + (i+1); fragment.appendChild(item); } list.appendChild(fragment); // Reflow once {/code} Document fragments are great because instead of adding themselves to the DOM when you append or insert them, they just add all their children. Think of them like storage containers in your DOM with no visual representation to cause Reflow! Layout styles applied

Yes nasty Reflow is caused every time you do any of the following

Style changes

{code type=php}
// slow
element.style.height = “100px”;
element.style.display = “block”;
element.style.fontsize = “130%”;
{/code}

All those amazing little style objects will need Reflow to happen, and if you coded your changes like I have above, you will cause 3 Reflows. Which is unnecessarily slow, below is a much better way of doing the same thing.

Style changes

{code type=php}
// fast

// css
.active {
height: 100px;
display: block;
font-size: 130%;
}

// script
element.className = “active”;

{/code}

As long as your grouping things together as above you will be fine.

So in Summary to speed up your DOM

  • Be careful using HTML collection objects
  • Perform DOM manipulation off the document
  • Change CSS classes, not CSS styles

So just remember that when touching the DOM there can be side effects, you just need to effectively manage how you touch it and when. Follow those rules and your web sites will be snappy, and your users will always be happy to surf about.

Ok wow, big post but an important one, so essentially I’ve compiled a 1 Hr long video into a few pages, I’ve obviously paraphrased and simplified a few things from the video so if you need more, check out the whole thing at “Google Talks -Javascript and speed“.

Now just to re-cap the key points

  • Mind your scope
  • Local variables are your friends
  • Function execution comes at a cost
  • Keep loops small
  • Avoid doing work whereever possible

Thanks guys, let me know how you go!

Pages: 1 2 3 4

2 Comments

  1. Very informative article. I guess it’s simple tips like this that make the difference between light and heavy code. Do these techniques apply to PHP too?

  2. CraigW says:

    I had a function which was taking over 37000ms to execute on IE6 (our company still insist on using IE6) and even 24,000ms in IE8 – Firefox executed the same function in 150ms.

    I discovered the cause – I was using a for – in to iterate around an array and it seems that IE is incapable of optimising for – in loops. I changed the code so it was using a for loop:

    OLD Code

    for (elementNo in myArray)
    {
    // for loop code
    }

    NEW Code

    var totalElements = myArray.length;
    for (var elementNo = 0; elementNo < totalElements; elementNo ++)
    {
    // for loop code
    }

    This reduced the IE6 time to 700ms (Firefox was 20ms faster).