Web Performance Calendar

The speed geek's favorite time of year
2021 Edition
ABOUT THE AUTHOR
Leon Fayer photo

Leon Fayer (@papa_fire) currently leads engineering at Teaching Strategies. Leon has over two decades of expertise concentrated on architecting and operating complex, web-based systems to withstand crushing traffic (often unexpectedly). Over the years, he's had a somewhat unique opportunity to design and build systems that run some of the most visited websites in the world and has the opinion that nothing really works until it works for at least a million people.

Loops are generally not great from the performance perspective, but often times it’s relatively easy to pinpoint and optimize those inefficient function you wrote and call inside of a loop. But sometimes, especially with JavaScript, the functions causing the performance hit are the ones that are provided to you by language.

Let’s look at the most basic, old school, example of taking an array of items and adding them to your html table.

for (let i = 0; i < items.length; i++) {
  document.getElementById("listGrid").innerHTML += "<tr><td>" + 
                                                   items[i].name + 
                                                   "</td></tr>";
}

This is pretty straight forward. Iterate through the array and inject each item name into a listGrid table . There are no special functions to worry about, this loop is based purely on JavaScript-provided functions for doing exactly those things. And yet, there is still room for optimization.

Reduce property lookup overhead

First of all, let’s look at the loop definition itself. The instruction is to iterate from index 0 to last element of the loop, as defined by the array function length. Problem here is that items.length property is being accessed on every iteration of the loop, for really no reason. So let’s take it outside the loop.

const itemArrLen = items.length;
for (let i = 0; i < itemArrLen; i++) {
  document.getElementById("listGrid").innerHTML += "<tr><td>" + 
                                                   items[i].name + 
                                                   "</td></tr>";
}

Now, no matter how many elements are in that array, items.length will only be looked up once.

Reduce DOM access

The other issue with the example code is the injection action itself. document.getElementById traverses DOM with each loop iteration to find the same listGrid element every time. Instead, let’s just do the lookup once, store the element in a variable and use it in a loop for injection.

const itemArrLen = items.length;
const element = document.getElementById("listGrid");
for (let i = 0; i < itemArrLen; i++) {
  element.innerHTML += "<tr><td>" + items[i].name + "</td></tr>";
}

Note, the same applies if you’re using jQuery (or other frameworks) instead of raw HTML for DOM manipulation. Remember, no matter how you do DOM lookups – you’re still doing DOM lookups.

And reduce DOM access again

Finally, let’s address the element.innerHTML property access. What happens now is we’re looking up the `innerHTML` property at every interaction. That’s one problem. The second problem is that we’re changing the property value with every loop tick, which means updating the DOM, which means a potential (re)layout by the browser. Modern browsers are smart not to do too many layouts and batch them (as they are expensive), however it all depends on what else is going on in the page/app. If another piece of code decides to request layout information (e.g. get the height of some element), the browser has no choice but to flush the queue of batched updates. Ouch.

The solution to both problems is simple: use a local variable for all the string concatenation. Once done, touch the DOM only once to update it:

const itemArrLen = items.length;
const element = document.getElementById("listGrid");
let newHTML = '';
for (let i = 0; i < itemArrLen; i++) {
  newHTML += "<tr><td>" + items[i].name + "</td></tr>";
}
element.innerHTML += newHTML;

One Response to “Reducing Property Access”

  1. Danilo

    Great advice!

Leave a Reply

You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>
And here's a tool to convert HTML entities