59

For the purpose of this question lets say we need to append() 1000 objects to the body element.

You could go about it like this:

for(x = 0; x < 1000; x++) {
    var element = $('<div>'+x+'</div>');
    $('body').append(element);
}

This works, however it seems inefficient to me as AFAIK this will cause 1000 document reflows. A better solution would be:

var elements = [];
for(x = 0; x < 1000; x++) {
    var element = $('<div>'+x+'</div>');
    elements.push(element);
}
$('body').append(elements);

However this is not an ideal world and this throws an error Could not convert JavaScript argument arg 0 [nsIDOMDocumentFragment.appendChild]. I understand that append() can't handle arrays.

How would I using jQuery (I know about the DocumentFragment node, but assume I need to use other jQuery functions on the element such as .css()) add a bunch of objects to the DOM at once to improve performance?

5
  • append() does not accept an array of strings. Commented Aug 24, 2012 at 14:55
  • @GeorgeReith the answer you approved is even slower than your first approach. Take a look at the console logs in this jsfiddle jsfiddle.net/du2TN/2 Commented Aug 24, 2012 at 15:23
  • @davids interesting, although it seems my original code works in jQuery 1.8 and is the fastest of the jQuery methods. (updated your JSfiddle) Commented Aug 24, 2012 at 15:29
  • Nice to know :) Anyway, @jAndi and jackwander's solution is much faster. But if you prefer to use jQuery, whatever fits you :) Commented Aug 24, 2012 at 15:35
  • @davids I need to use jQuery as they must be jQuery objects so that I can use other jQuery functions in them. Commented Aug 24, 2012 at 16:57

9 Answers 9

72

You could use an empty jQuery object instead of an array:

var elements = $();
for(x = 0; x < 1000; x++) {
    elements = elements.add('<div>'+x+'</div>');
    // or 
    // var element = $('<div>'+x+'</div>');
    // elements = elements.add(element);
}
$('body').append(elements);

This might be useful if you want to do stuff with newly generated element inside the loop. But note that this will create a huge internal stack of elements (inside the jQuery object).


It seems though that your code works perfectly fine with jQuery 1.8.

Sign up to request clarification or add additional context in comments.

9 Comments

My actual testing solution is a little more complicated, each element should be a jquery object in its own right so that you can perform other jQuery functions on it. My bad for simplifying the test case too much. Updated question to reflect this.
But then using .add and inserting the final jQuery object should work nevertheless.
Ooops you are right, I forgot that add doesn't update the original copy.
@George: Apparently the .append behaviour changed with jQuery 1.8. It works with arrays of jQuery objects as well now. This is your code: jsfiddle.net/7Q6wm/2. I assume you used an older version.
doesn't work in older versions of jQuery, use the document.createDocumentFragment method described by @jackwanders
|
17

You could just call

$('body').append(elements.join(''));

Or you can just create a large string in the first place.

var elements = '';
for(x = 0; x < 1000; x++) {
    elements = elements + '<div>'+x+'</div>';
}
$(document.body).append(elements);

Like you mentioned, probably the most "correct" way is the usage of a DocFrag. This could look like

var elements = document.createDocumentFragment(),
    newDiv;
for(x = 0; x < 1000; x++) {
    newDiv = document.createElement('div');
    newDiv.textContent = x;
    elements.append( newDiv );
}
$(document.body).append(elements);

.textContent is not supported by IE<9 and would need an conditional check to use .innerText or .text instead.

3 Comments

@JosephSilber: nothing wrong with using += of course. I just wanted to be more explicit and expresive here.
Thanks Andy but I fear I oversimplified my test case (my bad), I should of made each element its own object so that you can perform jQuery functions like .css() on them. I updated the test case in the question.
Shouldn't that be newDiv.textContent = x;?
10

Upgrade to jQuery 1.8, this works as intended:

​$('body')​.append([
    '<b>1</b>',
    '<i>2</i>'   
])​;​

Comments

9

Since $.fn.append takes a variable number of elements we can use apply to pass the array as arguments to it:

el.append.apply(el, myArray);

This works if you have an array of jQuery objects. According to the spec though you can append an array of elements if you have the DOM elements. If you have an array of html strings you can just .join('') them and append them all at once.

1 Comment

Thanks. This is the answer if you're stuck with JQuery < 1.8
3

A slight change to your second approach:

var elements = [],
newDiv;
for (x = 0; x < 1000; x++) {
    newDiv = $('<div/>').text(x);
    elements.push(newDiv);
}
$('body').append(elements);

$.append() certainly can append an array: http://api.jquery.com/append/

.append(content) | content: One or more additional DOM elements, arrays of elements, HTML strings, or jQuery objects to insert at the end of each element in the set of matched elements.

1 Comment

jQuery was very strict in this regard. When it says "array of elements", it really means DOM elements, not jQuery objects. And indeed, using 1.7.2, this code does not work, but it works in 1.8.
3

Sometimes, jQuery isn't the best solution. If you have a lot of elements to append to the DOM, documentFragment is a viable solution:

var fragment = document.createDocumentFragment();
for(var i = 0; i < 1000; i++) {
    fragment.appendChild(document.createElement('div'));
}
document.getElementsByTagName('body')[0].appendChild(fragment);

2 Comments

Doesn't jQuery use document fragment internally in the case you pass in an array? From the error above it definitely seems so.
Thanks! This worked for me; I'm working on a project that is using an older version of jQuery that didn't handle arrays but works fine with createDocumentFragment.
0

If you're going for raw performance then I would suggest pure JS, though some would argue that your development performance is more important than your site's/program performance. Check this link for benchmarks and a showcase of different DOM insertion techniques.

edit:

As a curiosity, documentFragment proves to be one of the slowest methods.

3 Comments

that jsPerf is wrapping documentFragment in a $() call, which is probably what's slowing it down. documentFragment is still useful if you go for a pure JS solution.
This is test is a weird mix of DOM and jQuery, I don't thing it is very conclusive.
It uses jQuery's each, but the actual appending is done using different methods.
0

I would use native Javascript, normally much faster:

var el = document.getElementById('the_container_id');
var aux;
for(x = 0; x < 1000; x++) {
    aux = document.createElement('div');
    aux.innerHTML = x;
    el.appendChild(aux);
}

EDIT:

There you go a jsfiddle with different options implemented. The @jackwander's solution is, clearly, the most effective one.

2 Comments

you're still doing 1000 separate appendChild operations, which will be slow.
@jackwanders you are completely right, I've implemented your solution mine one and is clearly faster, it can be seen in the jsfiddle I've attached
0

I know, the question is old, but maybe it helps others.

Or simple use ECMA6 spread operator:

$('body').append(...elements);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.