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 |
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)
Хотелось бы увидеть результаты тех же тестов, но с использованием прототайпа ;)
Вот кстати ссылочка (еле вспомнил где читал ) на советы по оптимизации JS с описаниями “почему”, про innerHTML там кстати тоже есть ;)
За ссылку спасибо, довольно занятная статья. По поводу prototype – имеется в виду Insertion? Если да, то там используются методы DOM + туча проверок всякообразных…
Ага. Вот как раз и интересно на сколько упадет скорость из-за этих проверок.
Очень интересная статья.
Очень часто по работе приходится выбирать тот или иной метод решения задач и теряешь на этом кучу времени.
С нетерпением буду ждать продолжения
Ухты!!! Никогда бы не подумал, что innerHTML самый медленный. Я то, по наивности, думал, что если просто, то и быстро.
Продолжаю читать и благодарю за пост.
Не совсем innerHTML. Медленная только его модификация через +=. Если взять присвоить строку — то это самый быстрый способ подменить элементы.
Полезная статейка, но к большому сожалению многие программисты делаю “как проще”.
P.S. Try/catch – конструкция о которой не стоит забывать если не уверен в кроссплатформенности твоего решения. Try/catch позволяет писать код устойчивый к ошибкам.
Естественно, try/catch никто не отменял :-) Но вот в данном конкретном случае есть кроссплатформенное решение, которое еще и быстро работает. Особенно, когда нужно выжать максимум из браузера.
[…] После прочтения статьи Дмитрия Штефлюка про скорость работы javascript в разных браузерах я очень удивился – мой любимый Firefox оказался чуть ли не самым медленным браузером (после IE 6.0). Решил проверить самостоятельно на его тестовом примере и заодно сравнить скорость альфа-версии Firefox 3.0 с второй версией. А заодно установил Opera 9.20, которая вот вышла буквально вчера, чтобы удостовериться в ее быстроте. […]
Great article. Thank you very much.
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.
Thanks for this article, it save my day!
Thanks again!
[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]
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.
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?
можно заменить на
(ага, читаем Lecompte) — будет еще быстрее :)
Для IE7 метод innerHTML не прокатывает для вставки <option> в <select>. Обрезается первый открывающий тэг <option>, в результате ни один из элементов списка не отображается.
array[idx++]=
еще делают так, array[array.length]=
проверенно, работает быстрее чем .push
а склеивание строк с помощью отератора += тормазит не только в JS…