DamianS1987
DamianS1987

Reputation: 312

How to make js "for" loop more efficient?

I am wondering how to make the loop below more efficient. On my page it has more iterations than 100, that is why, as you can imagine it is troublesome.

    for (var i = 0; i < 1000; i += 1) {
        var el = document.createElement('div');
        el.appendChild(document.createTextNode('Node ' + (i + 1)));
        document.getElementById('nodeHolder').appendChild(el);
    }

Thanx for help in advance.

Upvotes: 1

Views: 332

Answers (6)

sErVerdevIL
sErVerdevIL

Reputation: 263

You definitely need to use DocumentFragment as suggested by @David Thomas. The most efficient version as I see is this... cloneNode is always better than createElement, switching between shallow or deep copy (still faster than createEl) as required.

Not that it gives a significant gain but you should shy away from assignments when you can. Store data in variables only when you need to. Of course when it comes to readability it's a different thing.

var fragment = document.createDocumentFragment();
var tplEl = document.createElement('div');
var contentTpl = document.createTextNode('Node ');

for (var i = 1; i <= 1000; i++) {
    var curNode = contentTpl.cloneNode(false);
    curNode.nodeValue = curNode.nodeValue + i;
    tplEl.cloneNode(false).appendChild(curNode);
    fragment.appendChild(tplEl);
}

document.getElementById('nodeHolder').appendChild(fragment);

Note that adding the fragment to the nodeHolder adds all childs of fragment as childs to nodeHolder triggering only 1 flow as opposed to 1000 flows in your earlier code.

Reference: Speeding up JavaScript

Upvotes: 1

Danny Hong
Danny Hong

Reputation: 1482

jQuery way...

var d = '';
for(var i=0;i<1000;i++) d += '<div>Node ' + (i + 1) + '</div>';
$('#nodeHolder').append(d);

Or javascript inside html...

<div id="nodeHolder">
<script>(function() { for(var i=0;i<1000;i++) document.write('<div>Node ' + (i + 1) + '</div>'); })();</script>
</div>

Upvotes: 2

David Thomas
David Thomas

Reputation: 253318

All I can really suggest is getting a reference to the nodeHolder element outside of the for:

var nodeHolder = document.getElementById('nodeHolder');

 for (var i = 0; i < 1000; i += 1) {
    var el = document.createElement('div'); 
el.appendChild(document.createTextNode('Node ' + (i + 1)));
    nodeHolder.appendChild(el);
 }

Or perhaps using a document fragment to hold the interim changes/appends and then add that to to the nodeHolder after the loop:

var fragment = document.createDocumentFragment();

for (var i = 0; i < 1000; i += 1) {
    var el = document.createElement('div'); 
    el.appendChild(document.createTextNode('Node ' + (i + 1)));
    fragment.appendChild(el);
}

document.getElementById('nodeHolder').appendChild(fragment.cloneNode(true));

JS Fiddle showing both approaches (with timing if the console is available).

Upvotes: 3

Matthew Blancarte
Matthew Blancarte

Reputation: 8301

In summary:

  1. Cache your DOM selector.
  2. Ditch the for loop, and go for a reverse while loop.
  3. Only append your element to the DOM once. The DOM is almost always the bottleneck.

In this pattern, you can take advantage of a reverse loop:

//Cache the DOM element
var node = document.getElementById('nodeHolder'),
    markup = [];

//Run the loop backwards for additional speed
var i = 10000;
while(i--){
  markup.push('<div>Node ' + (i + 1) + '</div>');
}

//Reverse the array, join it, then set the innerHTML
node.innerHTML = markup.reverse().join('');​

Working example: http://jsfiddle.net/ZAkMZ/3/

Reverse while loop speed reference: https://blogs.oracle.com/greimer/entry/best_way_to_code_a

jQuery version:

//Cache the DOM element
var node = $('#nodeHolder'),
    markup = [];

//Run the loop backwards for additional speed
var i = 10000;
while(i--){
  markup.push('<div>Node ' + (i + 1) + '</div>');
}

//Reverse the array, join it, then set the innerHTML
node.append(markup.reverse().join(''));​

Upvotes: 1

Jai
Jai

Reputation: 74738

May be this way:

for (var i = 0; i < 100; i+=1) {
  $("<div/>").appendTo("body").attr({"class":'txtHolder'});
  $(".txtHolder").append(i+1);            

}​

Upvotes: 0

mxro
mxro

Reputation: 6878

Maybe: You could generate a HTML String in the for loop like:

<div>Node 1</div><div>Node 2</div>

and then set the .innerHTML property of nodeHolder to this string after the whole loop is completed.

(... and let the DOM renderer figure out how to best optimize the action)

Upvotes: 1

Related Questions