JavaScript optimization Part 1: Adding DOM elements to document

Posted by Dmytro Shteflyuk on under JavaScript

Most web-developers writing tons of JavaScript, especially in our Web 2.0 century. It’s powerful technology, but most browsers has very slow implementation of engine, and everyone at some instant decide to review code and make it faster. In this post I’ll share my experience and explain several tricks how to make your JavaScript as fast as possible.

This is first article in 7 parts tutorial, stay tuned.

Scenario: you’re developing rich Internet application and you need to load dynamic elements using AJAX and then add them to current document. For some reason you don’t want (or can’t) use fully generated HTML, and decided to fetch items in JavaScript array.

I know two classic ways to do so: create elements using document.createElement() method and concatenate HTML string and assign it to parentElement.innerHTML property. Of course, you can combine both ways. Let’s examine this ways in details.

Classic way (and in ideal world the best way) is to use DOM for element manipulations:

1
2
3
4
5
for (var i = 1; i <= 1000; i++) {
    var li = document.createElement('li')
    li.appendChild(document.createTextNode('Element ' + i));
    el.appendChild(li);
}

Not so bad performance. Internet Explorer 6 is slowest – 1403 ms (but it is a slower browser in the world, right?), and other browser displayed results quickly (63 – 328 ms). Ok, but what about creating DOM element from HTML code?

1
2
3
4
for (var i = 1; i <= 1000; i++) {
    var li = document.createElement('<li>Element ' + i + '</li>');
    el.appendChild(li);
}

It works better in Internet Explorer 6 (1134 ms), but does not work in other browsers at all. Weird! Of course, you can add try/catch block and create elements using first approach in catch block for the other browsers. But I have better solution.

Every DOM node has attribute innerHTML which holds all child nodes as HTML string.

1
2
3
4
el.innerHTML = '';
for (var i = 1; i <= 1000; i++) {
    el.innerHTML += '<li>Element ' + i + '</li>';
}

Wow, I’m highly impressed how slow could be adding elements procedure (11391 – 307938 ms)! Cool result, right? It’s because browser tried to render list while we updating and it’s take so long time. Little optimization:

1
2
3
4
5
var html = '';
for (var i = 1; i <= 1000; i++) {
    html += '<li>Element ' + i + '</li>';
}
el.innerHTML = html;

All browsers shows great perfomance (31 – 109 ms), but Internet Explorer is still slow – 10994 ms. I found solution which works very fast in all browsers: to create array of HTML chunks, and the join them using empty string:

1
2
3
4
5
6
7
var html = [];
for (var i = 1; i <= 1000; i++) {
    html.push('<li>Element ');
    html.push(i);
    html.push('</li>');
}
el.innerHTML = html.join('');

It’s fastest approach for Internet Explorer 6 – 400 ms, and very fast in other browsers. Why I’m not saying fastest in case of Firefox? I added another test to make in cleaner:

1
2
3
4
5
6
7
var html = '';
for (var i = 1; i <= 1000; i++) {
    html += '<li style="padding-left: ' + (i % 50) +
    '" id="item-' + i + '">Element ' + i + ' Column ' +
    (i % 50) + '</li>';
}
el.innerHTML = html;

And second example:

1
2
3
4
5
6
7
8
9
10
11
12
13
var html = [];
for (var i = 1; i <= 1000; i++) {
    html.push('<li style="padding-left: ');
    html.push(i % 50);
    html.push('" id="item-');
    html.push(i);
    html.push('">Element ');
    html.push(i);
    html.push(' Column ');
    html.push(i % 50);
    html.push('</li>');
}
el.innerHTML = html.join('');

Here are the results in table and diagram formats.

No Method IE 6 IE 7 FF 1.5 FF 2.0 Opera 9
1 createElement() 1403 219 166 328 63
2 createElement() full 1134 - - - -
3 innerHTML 39757 20781 41058 307938 11391
4 innerHTML optimized 10994 46 50 109 31
5 innerHTML/join 400 31 47 125 31
 
6 innerHTML/optimized+ 28934 109 84 172 62
7 innerHTML/join+ 950 78 110 189 62

Benchmark: Adding DOM elements to document

You can view benchmark test and get your own results here.

Conclusions

  • Always use DOM node functions to keep your code standards-compliant. This approach has satisfactory performance and works in all browsers.
  • If you need extremely high performance, use join+innerHTML method, which has best time in benchmark.
  • Never use appending HTML strings to the innerHTML (yeah, if you need append one small element).
  • Opera is fastest browser in the world, but Internet Explorer 7 is fast too, Firefox 2.0 surprised me with his low performance.
  • Never believe fanatics like me and benchmark different approaches by yourself (but don't worry, Microsoft does not paid me for their browser advertisement).

Links to other parts

  • Part 1: Adding DOM elements to document
  • Part 2: Applying styles to elements
  • Part 3: Attaching events
  • Part 4: Multiple anonymous functions (will be published soon)
  • Part 5: Attaching events on demand (will be published soon)
  • Part 6: Element hide and show (will be published soon)
  • Part 7: Elements collection enumeration (will be published soon)

18 Responses to this entry

Subscribe to comments with RSS

said on March 25th, 2007 at 04:41 · Permalink

Хотелось бы увидеть результаты тех же тестов, но с использованием прототайпа ;)

Вот кстати ссылочка (еле вспомнил где читал ) на советы по оптимизации JS с описаниями “почему”, про innerHTML там кстати тоже есть ;)

said on March 25th, 2007 at 04:53 · Permalink

За ссылку спасибо, довольно занятная статья. По поводу prototype – имеется в виду Insertion? Если да, то там используются методы DOM + туча проверок всякообразных…

said on March 25th, 2007 at 05:30 · Permalink

Ага. Вот как раз и интересно на сколько упадет скорость из-за этих проверок.

said on March 25th, 2007 at 12:49 · Permalink

Очень интересная статья.

Очень часто по работе приходится выбирать тот или иной метод решения задач и теряешь на этом кучу времени.

С нетерпением буду ждать продолжения

said on March 27th, 2007 at 01:10 · Permalink

Ухты!!! Никогда бы не подумал, что innerHTML самый медленный. Я то, по наивности, думал, что если просто, то и быстро.

Продолжаю читать и благодарю за пост.

said on March 27th, 2007 at 01:15 · Permalink

Не совсем innerHTML. Медленная только его модификация через +=. Если взять присвоить строку — то это самый быстрый способ подменить элементы.

said on March 27th, 2007 at 10:55 · Permalink

Полезная статейка, но к большому сожалению многие программисты делаю “как проще”.
P.S. Try/catch – конструкция о которой не стоит забывать если не уверен в кроссплатформенности твоего решения. Try/catch позволяет писать код устойчивый к ошибкам.

said on March 27th, 2007 at 10:57 · Permalink

Естественно, try/catch никто не отменял :-) Но вот в данном конкретном случае есть кроссплатформенное решение, которое еще и быстро работает. Особенно, когда нужно выжать максимум из браузера.

said on April 12th, 2007 at 13:10 · Permalink

[…] После прочтения статьи Дмитрия Штефлюка про скорость работы javascript в разных браузерах я очень удивился – мой любимый Firefox оказался чуть ли не самым медленным браузером (после IE 6.0). Решил проверить самостоятельно на его тестовом примере и заодно сравнить скорость альфа-версии Firefox 3.0 с второй версией. А заодно установил Opera 9.20, которая вот вышла буквально вчера, чтобы удостовериться в ее быстроте. […]

Erhan
said on May 22nd, 2007 at 22:33 · Permalink

Great article. Thank you very much.

Sören
said on June 29th, 2007 at 08:09 · Permalink

thanx for the article! interesting – i never thought of an array and “push” to do this. btw “push” is not compatible with ie5.0 if you care about that browser.
but one important thing – i think your ie6 install must be totally f***** up. i realy could not reproduce your measurements…
on my machine for ie6 (in test-order):
188, 125, (too long ;-), 94, 78
to get a comparison, ff1.5 takes 150ms for the first test here… so?!
to use innerHTML was also the recommendation of PPK (Peter Paul Koch), after his testings way back.
hm, there is clearly something wrong.

DrGreen
said on July 5th, 2007 at 11:37 · Permalink

Thanks for this article, it save my day!
Thanks again!

said on August 14th, 2007 at 10:46 · Permalink

[lang_ru]Ну на счет opera – самы быстрый браузер – я бы не согласился. Safari рулит, потестируй. Минимум в 2 раза быстрее всех.[/lang_ru]
[lang_en]Can’t agree about Opera is the fastest browser in the world. Try out Safary and see that it’s at least two times faster than other.[/lang_en]

efreeti
said on December 21st, 2007 at 15:53 · Permalink

I don’t quite understand what it has to do with JavaScript optimization? The only JavaScript optimization I see here is using array for string concat. The rest you were talking about is optimization of interaction with DOM ( and to tell you honestly nothing very new). Still although this method is more descriptive than using DOM methods for creation of JS, it is still ugly. In the end HTML markup should never be bundled inside the JS code, it’s already a bad practice – visualization should be separate from the logic. According to current state of things and MVC HTML is a part of the view, which is why you should not mess its creation with control (JS). So such things as HTML template should be defined outside the script. There are different ways of implementing such templating mechanism, some frameworks support such options.
You can say that you don’t have time for that and you fine with this solution. In case of small script/hack you will be right. But in any case this is far from unobtrusive JavaScript or good practise in case of larger development.

efreeti
said on December 21st, 2007 at 16:14 · Permalink

BTW rest of your articles are also only connected to HTML/DOM/CSS/JS combination and not JS itself. I think it’s incorrect to name your article this way. Aren’t there quite useful topics like optimization of lookups, state pattern for avoiding if statements, etc. that really concern JS coding practices?

said on April 29th, 2008 at 06:13 · Permalink
1
array.push

можно заменить на

1
array[idx++]=

(ага, читаем Lecompte) — будет еще быстрее :)

Abadonna
said on August 29th, 2008 at 16:58 · Permalink

Для IE7 метод innerHTML не прокатывает для вставки <option> в <select>. Обрезается первый открывающий тэг <option>, в результате ни один из элементов списка не отображается.

mascon
said on August 31st, 2008 at 16:56 · Permalink

array[idx++]=
еще делают так, array[array.length]=
проверенно, работает быстрее чем .push
а склеивание строк с помощью отератора += тормазит не только в JS…

Comments are closed

Comments for this entry are closed for a while. If you have anything to say – use a contact form. Thank you for your patience.