tags:

views:

1330

answers:

8

I'm looking at a problem that needs a complex block of divs to be created once for each element in a set of ~100 elements.

Each individual element is identical except for the content, and they look (in HTML) something like this:

<div class="class0 class1 class3">
<div class="spacer"></div>
<div id="content">content</div>
<div class="spacer"></div>
<div id="content2">content2</div>
<div class="class4">content3</div>
<div class="spacer"></div>
<div id="footer">content3</div>
</div>

I could either:

1) Create all the elements as innerHTML with string concatenation to add the content.

2) Use createElement, setAttribute and appendChild to create and add each div.

Option 1 gets a slightly smaller file to download, but option 2 seems to be slightly faster to render.

Other than performance is there a good reason to go via one route or the other? Any cross-browser problems / performance gremlins I should test for?

...or should I try the template and clone approach?

Many thanks.

+2  A: 

Personally, I use innerHTML because it's what I'm used to and for something like this, the W3C methods add a lot of clutter to the code.

Just a possible way to cut down on the number of div's however, are there any reasons you are using spacer elements instead of just editing the margins on the content divs?

Macha
+1  A: 

I don't think there's much to choose between them. In the olden days (IE6, FF1.5), innerHTML was faster (benchmark), but now there doesn't seem to be a noticeable difference in most cases.

According to the mozilla dev. docs there are a few situations where innerHTML behaviour varies between browsers (notably inside tables), so createElement will give you more consistency - but innerHTML is usually less to type.

Chris May
A: 

As I know, the fastest way is to evade DOM editing as long as it is possible. That mean, it is better to create a big string and then put it into innerHTML. But there is a remark for this: don't make too many operation on big strings, it is faster to use array of strings and then join them all.

Thinker
+13  A: 

Neither. Use a library like jQuery, Prototype, Dojo or mooTools because both of these methods are fraught with trouble:

The writers of the major javascript libraries have spent a lot of time and have entire bug tracking systems to make sure that when you call their DOM modifying tools they actually work.

If you're writing a library to compete with the above tools (and good luck to you if you are), then I'd choose the method based on performance, and innerHTML has always won out in the past, and since innerHTML is a native method, it's a safe bet it will remain the fastest.

altCognito
+4  A: 

altCognito makes a good point - using a library is the way to go. But if was doing it by hand, I would use option #2 - create elements with DOM methods. They are a bit ugly, but you can make an element factory function that hides the ugliness. Concatenating strings of HTML is ugly also, but more likely to have security problems, especially with XSS.

I would definitely not append the new nodes individually, though. I would use a DOM DocumentFragment. Appending nodes to a documentFragment is much faster than inserting them into the live page. When you're done building your fragment it just gets inserted all at once.

John Resig explains it much better than I could, but basically you just say:

var frag = document.createDocumentFragment();
frag.appendChild(myFirstNewElement);
frag.appendChild(mySecondNewElement);
...etc.
document.getElementById('insert_here').appendChild(frag);
Neall
A: 

For a complex problem like this, I usually use the innerHTML methods because they are a)easier to read and modify code-wise b)more convenient to use in loops. As the top post says, they unfortunately fail in IE(6,7,8) on <table>, <thead>,<tbody>,<tr>,<tfoot>, <select>, <pre> elements.

mike nvck
A: 

Since you mentioned template and clone, you may be interested in this question:

Another option is to use a DOM wrapper, such as DOMBuilder:

DOMBuilder.apply(window);
DIV({"class": "class0 class1 class3"},
  DIV({"class": "spacer"}),
  DIV({id: "content"}, "content"),
  DIV({"class": "spacer"}),
  DIV({id: "content2"}, "content2"),
  DIV({"class": "class4"}, "content3"),
  DIV({"class": "spacer"}),
  DIV({id: "footer"}, "content3")
);

Personally, if each item is going to need the exact same structure created I would go with the cloning approach. If there's any logic involved in creating the structure into which the content will go, I'd rather maintain something like the above than fiddling about with strings. If that approach turned out to be too slow, I'd fall back to innerHTML.

insin
A: 

1) Create all the elements as innerHTML with string concatenation to add the content.

2) Use createElement, setAttribute and appendChild to create and add each div.

3) compromise. Create all the elements in one go as innerHTML (which avoids a lot of childnode list manipulation slowness), then write the content that changes on each item using data/attribute access (avoiding all that nasty mucking around with having to HTML-escape content). eg. something like:

var html= (
    '<div class="item">'+
        '<div class="title">x</div>'+
        '<div class="description">x</div>'+
    '</div>'
);
var items= [
    {'id': 1, 'title': 'Potato', 'description': 'A potato'},
    {'id': 2, 'title': 'Kartoffel', 'description': 'German potato'},
    // ... 100 other items ...
];

function multiplyString(s, n) {
    var a= [];
    while (n-->0)
        a.push(s);
    return a.join('');
}

var parent= document.getElementById('itemcontainer');
parent.innerHTML= multiplyString(html, items.length);
for (var i= 0; i<items.length; i++) {
    var item= items[i];
    var node= parent.childNodes[i];

    node.id= item.id;
    node.childNodes[0].firstChild.data= item.title;
    node.childNodes[1].firstChild.data= item.description;
}

Can also be combined with Neall's tip about DocumentFragments.

bobince