10
X

43,439 reasons to use append() correctly

read 57 comments

The .append() method is perhaps the most misused of all jQuery methods. While an extremely useful and easy method to work with, it dramatically affects the performance of your page. When misused, the .append() method can cripple your JavaScript code's performance. When used well, it'll keep your script humming along.

Here is a typical example of append misuse:

JavaScript:
  1. var arr = reallyLongArray;
  2. $.each(arr, function(count, item) {
  3.     var newTd = $('<td></td>').html(item).attr('name','pieTD');
  4.     var newTr = $('<tr></tr>');
  5.     newTr.append(newTd);
  6.     $('table').append(newTr);
  7. });

Ran Once: Profile (308.697ms, 17861 calls, Firefox 3.06)
Ran in a for loop 100 times: Profile (51782.295ms, 1805100 calls)

Continue Reading Below

If you're coming from Prototype to jQuery, chances are this looks quite natural to you. It's the way that Prototype does node creation/insertion, but with a little bit of jQuery chaining thrown in. Let's see how we can improve this.

JavaScript:
  1. var arr = reallyLongArray;
  2. $.each(arr, function(count, item) {
  3.     var newTr = '<tr><td name="pieTD">' + item + '</td></tr>';
  4.     $('table').append(newTr);
  5. });

Ran Once: Profile (107.458ms, 3991 calls, Firefox 3.06)
Loop of 100: Profile (21641.336ms, 399100 calls)

Whoa! Nearly a 3x difference, and much simpler to look at. jQuery can take more then one node at a time and create them all at once. You also don't need to wrap it in $() before appending it. jQuery knows what to do. But wait, there's more!

JavaScript:
  1. var arr = reallyLongArray;
  2. var textToInsert = '';
  3. $.each(arr, function(count, item) {
  4.     textToInsert  += '<tr><td name="pieTD">' + item + '</td></tr>';
  5. });
  6. $('table').append(textToInsert);

Ran Once: Profile (30.792ms, 778 calls, Firefox 3.06)
Loop of 100: Profile (8505.37ms, 77800 calls)

Taking full advantage of the ability of jQuery to insert a chunk of html in a string means only having to call insert once. It's much quicker, with an approximately 9-10x speed increase from the initial algorithm! This will be fast enough for 95% of cases, but for strings with lots of string concatenation… Wait, there's more!

JavaScript:
  1. var arr = reallyLongArray;
  2. var textToInsert = [];
  3. var i = 0;
  4. $.each(arr, function(count, item) {
  5.     textToInsert[i++]  = '<tr><td name="pieTD">';
  6.     textToInsert[i++] = item;
  7.     textToInsert[i++] = '</td></tr>';
  8. });
  9. $('table').append(textToInsert.join(''));

Credit for this one goes to Michael Geary
Ran Once: Profile (29.724ms, 778 calls, Firefox 3.06)
Loop of 100: Profile (8298.699ms, 77800 calls)

This version is a bit harder to understand, as the html is not in an easy to read format, and the results vary by browser* (see below for further analysis), but in most current and next generation browsers, adding to an array and joining it into a string at the end is quicker then doing string concatenations. So, if you are looping through a large number of string concatenation and need some more speed, you should consider this method. So, now we're done, right? This is as blazingly fast as we can get it? Not quite.

JavaScript:
  1. var arr = reallyLongArray;
  2. var textToInsert = [];
  3. var i = 0;
  4. var length = arr.length;
  5. for (var a = 0; a <length; a += 1) {
  6.     textToInsert[i++]  = '<tr><td name="pieTD">';
  7.     textToInsert[i++] = arr[a];
  8.     textToInsert[i++] = '</td> </tr>' ;
  9.  
  10. }
  11. $('table').append(textToInsert.join(''));

Ran Once: Profile (29.72ms, 587 calls, Firefox 3.06)
Loop of 100: Profile (8274.359ms, 58700 calls)

As handy as .each() is for small loops, calling a function that executes a callback inside of a loop will always be slower than just creating the loop in pure JavaScript. The difference here isn't so noticeable because the array I used only has a length of 190, but for really large loops it makes a measurable difference. In addition, while the difference is very slight (.000062 vs .000037 ms over 1,000,000 empty loops), it is quicker to save the array length in a variable and use it instead of looking up an object property every loop (thanks Karl Swedberg!).

For most of your uses, the method of creating one really long string and appending it at the end will be the best choice, as it makes the best use of the trade offs of code legibility, ease of programming, and speed.

Please tell me your methods for speeding up array loops or appending using jQuery in the comments below.

For completeness, I used jQuery 1.3.2, Firebug 1.3.3 and Fireunit 1.0a1 on Firefox 3.06 on a Mac to do the profiling. And the reallyLongArray I used was:

var arr = [1, 2, 3, 4, 5, 'adsf', 'as', 6, 2, 3, 6, 'ffadscdasc', 'cada', 
'2wd', 3, 7, 3, 2, 'sdf', 1, 2, 3, 4, 5, 'adsf', 'as', 6, 2, 3, 6, 'ffadscdasc', 
'cada', '2wd', 3, 7, 3, 2, 'sdf', 1, 2, 3, 4, 5, 'adsf', 'as', 6, 2, 3, 6, 
'ffadscdasc', 'cada', '2wd', 3, 7, 3, 2, 'sdf', 1, 2, 3, 4, 5, 'adsf', 'as', 
6, 2, 3, 6, 'ffadscdasc', 'cada', '2wd', 3, 7, 3, 2, 'sdf', 1, 2, 3, 4, 5, 
'adsf', 'as', 6, 2, 3, 6, 'ffadscdasc', 'cada', '2wd', 3, 7, 3, 2, 'sdf', 1, 
2, 3, 4, 5, 'adsf', 'as', 6, 2, 3, 6, 'ffadscdasc', 'cada', '2wd', 3, 7, 3, 
2, 'sdf', 1, 2, 3, 4, 5, 'adsf', 'as', 6, 2, 3, 6, 'ffadscdasc', 'cada', '2wd', 
3, 7, 3, 2, 'sdf', 1, 2, 3, 4, 5, 'adsf', 'as', 6, 2, 3, 6, 'ffadscdasc', 
'cada', '2wd', 3, 7, 3, 2, 'sdf', 1, 2, 3, 4, 5, 'adsf', 'as', 6, 2, 3, 6, 
'ffadscdasc', 'cada', '2wd', 3, 7, 3, 2, 'sdf', 1, 2, 3, 4, 5, 'adsf', 'as', 
6, 2, 3, 6, 'ffadscdasc', 'cada', '2wd', 3, 7, 3, 2, 'sdf'];

Further analysis of += vs array.join, broken down by browser

---------------
Current gen Browsers
FF3 		array.join is ~2x faster
Safari 3 	array.join ~1.5x faster
Opera 9		+= ~3x faster
ie6		array.join ~6x faster
ie7		array.join ~4x faster
Next gen browsers
FF3.1		+= array.join equal in speed
Chrome		+=  ~1.25x faster
IE8		array.join ~1.6x faster
Webkit		array.join ~2x faster

Raw results

(2709 characters) 9 character string, added 300 times. Looped 100,000 times and averaged. +=
FF3.06 		.2046  -  .25835 .1849  .17045
Opera9 		.1256  -  .12795 .12678 .12197
Safari3 	.1368  -  .14875 .13128 .13032
IE6 		3.359 - took forever, crashed/froze browser sometimes
IE7 		1.68985 - took forever

FF3.1		.0735  -  .0813 .0710 .0683
Chrome 		.0256  -  .025 .021 .031
Webkit 		.0655  -  .06602 .06527 .06533
IE8		.2921  -  .3104 .2905 .2755 
array.join - called 300 times, looped 100,000 times and averaged.
FF3.06 		.1056  -  .10701 .10439 .10541
Opera9 		.3355  -  .33091 .31484 .36066
Safari3 	.0813  -  .08128 .08089 .08172
IE6  		.5086  -  .50609 .50829 .51156
IE7 		.4101  -  .42265 .38875 .41891

FF3.1		.0743  -  .0766 .0721 .0741
Chrome 		.0315  -  .03 .048 .027 .021
Webkit 		.0300  -  .02962 .03123 .02937
IE8		.1832  -  .1841.1872 .1872 .1741
(35109 character string) 351 character string added to a nine character string 100 times, looped 1000 times and averaged +=
FF3.06 		1.81	-   1.897 2.015 1.518
Opera9 		.6306	-   .631 .634 .627
Safari3 	.9156	-   .931 .884 .932
IE6 		65.657	-   58.078 56.641 89.313 58.594  // I can see where += was a huge problem then.
IE7 		.9316	-   .937 .934 .924

FF3.1		.9141	-   .47 .663 1.123 .705 1.165 1.361 .698 1.128
Chrome 		.031	-   .039 .024 .02 .041
Webkit 		.996	-   .991 1.014 .983
IE8		.09	-   .094 .094 .078 .094 
array.join the above
FF3.06		.9143	-  .923  .83 .99
Opera9		1.773	-  1.832 1.747 1.741
Safari3		.2106	-  .249 .19 .193
IE6		.8368	-  2 .375 .469 2.078 .343 .36 .735 .625 .547
IE7		.4428	-  .531 .516 .391 .375 .438 .406 .609


FF3.1		1.070	-  1.221 .678 1.366 1.508 .665 1.423 .631
Chrome		.0318	-  .02 .022 .059 .026
Webkit		.1773	-  .175 .176 .181
IE8		.4820	-  .14 1.141 .156 .609 .625 .328 .375


comment feed

57 comments

  1. Josh Powell

    In addition, I just read up that appending a list of <tr>s with a <tbody> around them is significantly faster then appending the <tr>s without the <tbody>.

    http://groups.google.com/group/jquery-en/browse_thread/thread/9889ebd5e10c9122

    My tests confirm this, and show a 6x speed difference between

    var newTr = '';
    for (var a = 0; a < 1000; a++) {
            newTr += ('<tr>test</tr>');
    }
    var start = new Date();
    $('#testTable').append(newTr);
    

    and

    var newTr = '';
    for (var a = 0; a < 1000; a++) {
            newTr += ('<tr>test</tr>');
    }
    $('#testTable').append('<tbody>' + newTr + '</tbody>');
    

    This applies not just to tr tags inside of a tbody, but putting any group of elements inside of one container element before appending it will be faster. So, appending <div></div><div></div><div></div> is slower then <div><div></div><div></div><div></div></div

    • kralco626

      This is likely because it's trying to do one DOM call for each top level element. Wrapping limits the number of top level elements to one. Not sure why it's implemented this way however...

  2. James

    I started the groups post with the question. Glad to see it led some inspiration! I've learned a lot from it also. :) Thanks also to Learning Jquery for the Working With Events post regarding Events Delegation. That was really, really helping on attaching events on the huge table.
    I've also mentioned removing all the data from the DOM also, which would be a good thing to write up on too if you gather enough about it. :)

  3. Wow, that's really surprising as I've done some optimization on a sort algorithm recently and when I tried to write back the complete sorted DOM it was terribly slow but when I broke it down to do an append for every row my speed increased significantly.

    Code looked kind of like this:

    var retailers = $("li.r");
    //...sort code
    //this:
    retailers.each(function(){
    $(this).appendTo("#retailers_list");
    });
    //was much faster(40-60%) than this:
    $("#retailers_list").append(retailers);

    Do you have any idea why that would be?

    • Josh Powell

      It looks like you are appending a jQuery collection of DOM nodes, which is different then appending a string containing html code.

      $("#retailers_list").append(retailers);

      There you are looping using the append method to loop through each DOM node in retailers and using append on it. It seems that is slower then looping though each DOM node in the jQuery collection and appending it to #retailers_list.

      Two things come to mind that may make this quicker:
      1) cache $('#retailers_list') so it does not need to select it every time (not sure if jQuery does this automatically or not)

      var rList = $('#retailers_list');
      retailers.each(function(){
          $(this).appendTo(rList);
      });
      

      2) It looks like var retailers = $("li.r") is already on the page, not sure how it got there but writing the html in a string like in the article and then appending it all at once could be quicker. But that depends how it got on the page to begin with...

      • 2. As stated this is a sort function. The html is on the page at load. I copy the DOM, sort, then write it back replacing the previous html. I'll try caching the selector.

  4. The thing that always bothers me about these examples is that the item being added and the name attribute are both "well behaved". In this case it appears the author intends item to be some arbitrary html (based on the .html) call at the top, so it is somewhat excusable. However, using .text() and .attr() handle the escaping for you, and presumably, that is where some of the expense of those calls comes from.

  5. k3n

    How's this one for speed?

    var arr = reallyLongArray;
    var tr = [ '<tr><td name="pieTD">', '</td></tr>' ];
    var textToInsert = tr[0] + Array.prototype.join.apply(arr, [tr[1] + tr[0]]) + tr[1];
    $('table').append( textToInsert );

    p.s. The last example in the article is probably gimped from the start -- the example doesn't close the tags (which I believe forces the browser to do so, which would be slower).

    • k3n: The missing </tr> in the last example was due to a problem with the output scrubbing that a couple WordPress plugins are doing when applying the syntax highlighting. The actual test closed the tags correctly.

  6. Also jquery developers who work with long list looping might find this article very useful.

    http://jquery-howto.blogspot.com/2009/02/5-easy-tips-on-how-to-improve-code.html

    It gives 5 tips to improve your jquery code performance when working with huge data sets.

  7. Another speed enhancement would be to remove the table from the DOM, do your appending, and then insert it back into the DOM - I heard (but have not tested) that this offers significant performance improvements.

  8. I was just wondering if these numbers might even be better your using a pre-dimensioned textToInsert array or arrayObject.push(). It seems to me that dynamic resizing of an array would take extra time and work and might result in quite a bit of data moving around but I have not looked too close into JavaScript core array handler code so it would just be a guess.

  9. Josh Powell

    @ Ben Bennett - A good point, in the cases where you need to escape html this would need to be considered.

    @k3n - How would you loop that, say, 100 times with different content in the td for each loop?

    @James - I haven't tried it, but it seems like this would be much slower as you are doing three operations - removing it, appending to it, and then appending it back to the DOM. Would love to see some numbers though.

    @ Kevin Pirkl - from what I've seen, array.push() is slower then array[i++] = and in javascript arrays cannot be pre-dimensioned, they all dynamically resize.

    • k3n

      @Josh: I'm confused on what you're asking for? The provided script does exactly the same thing as the other scripts on this page. If you would like to utilize it against a different implementation scenario, then please provide a new/updated baseline example. As-is, it will create a new TD per every array item in the "reallyLongArray".

      Also, why would you want to loop it? That's the beauty of maximizing the Array objects built-in prototype methods against some functional aspects of JS: iteration is handled intrinsically. However, if say you wanted to loop it 100 times for testing.....just wrap the whole thing in a for()?

      Sorry if I've missed your point on that comment.

      • Josh Powell

        @k3n I'm not trying to be snippity or critical here, I've just not seen this approach used before and am interested in how you would insert dynamic content into each <td></td> along the way because I know how to do that with a for loop and +=
        for (var a = 0; a < array.length; a++) {
        myString += '<td>' + array[a].attribute + '</td>';
        }

        Thank you for all of your comments though, glad to see that you are interested.

      • k3n

        @Josh

        Yes, if you need to access methods or properties of each item in the array, instead of simply the array item itself, then my method wouldn't quite work. However, given an array (such as the example), and all you need to do is to wrap some markup around each element, I believe the method I provided will be the fastest (on my simple benchmarks it's 25-30% faster than the 'fastest' implementation given thus far).

        As for the technique, I've actually lifted it from others much more knowledgable than I (Resig, Crockford).

      • Josh Powell

        Profiling it in Firefox 3.06 with jQuery 1.3.2 on a mac, I am getting

        50.691ms, 610 calls | 51.026ms, 610 calls | 50.732ms, 610 calls

        for

        var arr = reallyLongArray;
        var tr = [ '<tr><td name="pieTD">', '</td></tr>' ];
        var textToInsert = tr[0] + 
           Array.prototype.join.apply(arr, [tr[1] + tr[0]]) + tr[1];
        $('table').append( textToInsert );
        

        and 50.711ms, 610 calls | 51.307ms, 610 calls | 50.805ms, 610 calls for

        var arr = reallyLongArray;
        var textToInsert = [];
        var i = 0;
        var length = arr.length;
        for (var a = 0; a < length ; a += 1) {
            textToInsert[i++]  = '<tr><td name="pieTD">';
            textToInsert[i++] = arr[a];
            textToInsert[i++] = '&lt/td>&lt/tr>' ;
        }
        
        $('table').append(textToInsert.join(''));
        

        I think the array is too small to make a difference. Putting it inside of a <tbody> before appending made a much bigger difference, cutting the time to 24.786ms. So, I'm going to say that you are correct, your method is quicker, but not by enough to convince me to switch over to it except for huge arrays, at least for Firefox. I imagine this is browser dependent, perhaps one of the other browsers is much quicker with this method.

      • Josh Powell

        k3n - You got me really interested in your suggestion, so I spent a couple of hours researching it and here are my results.

        In an array of length 50,000 filled with numbers and small strings, this takes about 51ms

        var tr = [ '<tr><td name="pieTD">', '</td></tr>' ];
        var textToInsert = tr[0] + 
          Array.prototype.join.apply(arr,  [tr[1] + tr[0]]) + tr[1];

        compared with about 81 ms for

          var textToInsert = [];
            var i = 1;
            var length = arr.length;
            for (var a = 0; a < length; a += 1) {
                textToInsert[i++] = '<tr><td name="pieTD">';
                textToInsert[i++] = arr[a];
                textToInsert[i++] = '</td></tr>';
            }
            textToInsert.join('');
        

        However, both methods take just 17 ms on an array of 1000; So, it appears that method is quicker on very large arrays, but the same speed for most practical use cases.

        Some other things I discovered: Instead of Array.prototype.join.apply(arr, [tr[1] + tr[0]]) + tr[1]; you can use Array.prototype.join.call(arr, tr[1] + tr[0]) + tr[1]; and save the 1 ms it takes to convert tr[1] + tr[0] into an array to pass to .apply(). Obviously, not a big deal.

        You can also do tr[0] + arr.join(tr[1] + tr[0]) + tr[1]; instead of referencing the prototype and using call or apply, no speed difference either way. Also, you can shorten the entire thing to one line using strings instead of a separate array to hold them, no change in speed:

        '<tbody><td name="pieTD">' + arr.join('</td></tr><tr><td name="pieTD">')+
        '</td></tr></tbody>'

        And finally, you can even append it in one line

        $('table').append('<tbody><td name="pieTD">' + 
        arr.join('</td></tr><tr><td name="pieTD">')+
        '</td></tr></tbody>');
        

        Definitely the 'least code written' way to do it :)

  10. husayt

    Could you also compare this one to your results. It's faster for me.

    var arr = reallyLongArray;
    var textToInsert = ['<tr><td name="pieTD">'];
    var i = 1;
    var length = arr.length;
    for (var a = 0; a < length; a += 1) {
    textToInsert[i++] = arr[a];
    textToInsert[i++] = '</td> </tr><tr><td name="pieTD">';
    }
    textToInsert.pop();
    $('table').append(textToInsert.join(''));

  11. wade

    I find it curious that developers were drawn to tools like jQuery because they abstracted away this sort of thing, yet now that adoption is high people that never did it pure js are looking for speed and moving back in the direction of string concats and straight js.

    Just for speed comparison sake have you tried this test using innerHTML and createElement instead of anything jQuery? I don't think I would want to return to those days, but it would be interesting to know the cost of using jQuery at all. As a side effect of doing that test it would also show the speed diff between those two old rivals, the "correct" DOM approach vs the "hack" string injection. In browsers a few years back the hack always beat the DOM, don't know if that is still the case today.

    p.s. How many 'table' are used in the test? Without native CSS selectors that bit is a pain to do native; and will definitely factor in to the testing.

    • Josh Powell

      @wade - Oh, I would never want to move back to straight js to replace .append() unless speed was a huge priority. The reason harkens back to Ben Bennets comments about text() and attr(), append() abstracts away cross browser issues and manages event handlers registration. But yes, innerHTML is faster then .append() but I'm not sure it is by enough to make much of a difference since things are only appended once. createElement on the other hand, is just too much of a pain and it is much easier to write, much easier to read, and a lot more maintainable to just write the html in a string and .append() that string.

      People concentrating on the speed of javascript with jQuery shows the level of acceptance of developers of the library.

      • wade

        Thank you for your reply, and I admit to playing a bit of devils advocate with my comment. I think what gives me pause the most when looking at your example is the horrors of innerHTML from the late 90s early 00s. It becomes a slippery slope when HTML slips in to code, support for projects gets more difficult as strings of text in multiple files are combined to make the UI. When done properly it is really just semantics; yet over the years I have consistently seen less errors when developers think of the DOM as nodes and objects instead of concatenated strings.

        With that in mind, here is another take on your first example that moved the heavy append outside the loop:

        var arr = ["a","b","c","d","e","f","g","h"];
        var objAry = [];
        $.each(arr,function(ndx,val){
        objAry.push(
        $("<tr/>").append(
        $("<td>").html(ndx.toString()).attr("class","ndx"),
        $("<td>").html(val).attr("class","val")
        )
        );
        });
        $(objAry).appendTo("table");

        I think part of this post is about where to append, and part is what to append, your examples mixed them together so it is hard to tell which is causing more benefit. This snippet maintains the what, but changes the when.

  12. k3n

    @Josh re: pre-dimensioned JS arrays:

    Where did you get this information, that all JS arrays are dynamically dimensioned? This is directly from Firebug:

    >>> var x = new Array(5);
    >>> x;
    [undefined, undefined, undefined, undefined, undefined]

    IE corroborates this:

    var x = new Array(5);
    alert( x.length ); // alerts "5"

    • Josh Powell

      Hey Ken, yes this is very true but now we are into semantics as this is not doing what I would define as creating a static array.

      It creates an array with that many undefineds in it, rather like saying this for a variable:

      var a;  // a is undefined
      a += 'something';  // a's value is undefinedsomething

      If you array.push('something'); onto an array defined by new Array(5), 'something' does not go to the first part of the array, but rather in the 6th position. Another way to do the same thing is:

      var arr = [];
      arr.length = 5;
      arr.push('something');
      // [undefined, undefined, undefined, undefined, undefined,'something']

      or

      var arr = [];
      arr[5] = 'something'; 
      // [undefined, undefined, undefined, undefined, undefined, 'something']

      And the array is still dynamic as you can .push() or x[5] past the 5th position.

      All of that said, it is a very interesting question, if you do new Array(100) and then replace the undefineds by saying array[0] = 'something' and array[1] = 'something' is that quicker? Anyone want to test it :)

      • k3n

        Yes, that is essentially what I was getting to: I think it would be cheaper to intialize an array's size once, and then simply modify those existing values as opposed to re-dimensioning the array for each item.

      • Josh Powell

        Ok, tried it. It's actually marginally slower. ~91ms vs ~84ms.

  13. This has to be the BEST jquery learning resource I've come across. I'm going through every post starting from 2006, and am discovering great stuff all the time. Just wanted to thank you for all the great work here.

  14. Huh? Inserting HTML this way is the road to hell and IMO nobody should use it regardless. Use document.createElement (or document.CreateElementNS if you're using XHTML) and the other related DOM methods such as appendChild rather than trying to inject strings into your HTML.

    The proper DOM methods stop you from creating invalid HTML, and I find them simpler to follow when analysing code later (it's clear to see the HTML structure being created rather than trying to piece fragments of HTML together in your head).

    I can't actually see any good use-case for that append method in jQuery except as a crutch for developers who don't mind doing things the wrong way (or are ignorant that injecting HTML as a string is a bad thing to do).

    • Josh Powell

      Craig,

      You bring up an old battle but are presenting it rather one sided. W3C DOM node creation vs. innerHTML. Some developers do use createElement and appendChild rather than injecting strings using the innerHTML property. Unfortunately, they are slower across all browsers, and dramatically slower in ie6 and ie7) then innerHTML. The page is a bit out of date, but:

      http://www.quirksmode.org/dom/innerhtml.html

      DOM node creation might stop you from creating invalid html, but I really wouldn't consider that a big benefit. Nothing stops you from writing invalid html on the page, why is it so important to programmatically ensure validity when injecting it with javascript? Test your code. Besides, you can still write incorrect html, for example, by using appendChild to inject tr nodes into a table when a tbody is present instead of into the tbody.

      Personally, I find it much easier to read the string injection then the DOM node creation, if you find it the opposite, perhaps it is just because that is what you are used to. I'd say the same for me. But I suppose it may also depend on if you are more comfortable writing programming code or html.

    • GWT, which I'm used for my final year project seems to use innerHtml exclusively.
      So there must be a speed advantage.
      (GWT says they provides cross browser compatible and most optimized code)

      Any comments?
      (I'm just a newbie to JavaScript programming)

  15. Great tips ! I just found your site and can't stop reading :D . I've been using jQuery for some time now and it's great to see and learn new techniques.
    Stumbled !

  16. RoySolhaugen

    Most of the time and as far as I know, while loops are slightly faster then for loops so if your looking for speed this might be something you want to test, I have seen big improvements in Opera doing this.
    Don´t forget to put the length in a variable like the above examples, otherwise this might actually be slower :)

      
        var arr = reallyLongArray;
        var textToInsert = [];
        var i = 0;
        var length = arr.length;
        while (a < length) {
    	    textToInsert[i++]  = '<tr><td name="pieTD">';
    	    textToInsert[i++] = arr[a];
    	    textToInsert[i++] = '</td> </tr>' ;
    	    a +=1
         }
        $('table').append(textToInsert.join(''));
      
    
  17. mikhailov

    I'm using an append() method in my samples:
    http://railsgeek.com/my_jquery_samples.html

  18. RobG

    Josh,

    you've taken a ridiculously inefficient method of creating some DOM elements and found a faster way of doing it. Great. Nice to know that your "fastest" code using jQuery takes 7 times longer than the equivalent plain old javascript in Safari 4. In Firefox POJS is 8 to 10 times faster.

    Nothing you do with array.push or += or array[i++] or swapping for with while or do will alter that. It is no coincidence that removing jQuery from the code as much as possible speeds it up. If you really want to improve the speed, ditch jQuery and write a POJS function to do the job.

    Here's the POJS I used, nothing fancy at all:

    var tbody = document.getElementsByTagName('table')[0].tBodies[0];
    var frag = document.createDocumentFragment();
    var oRow = document.createElement('tr');
    var oCell = document.createElement('td');
    var cell, row;

    for (var i=0, ilen = arr.length; i<ilen; i++) {
    cell = oCell.cloneNode(false);
    cell.appendChild(document.createTextNode(arr[i]));
    row = oRow.cloneNode(false);
    row.appendChild(cell);
    frag.appendChild(row);
    }
    tbody.appendChild(frag);

    • Josh Powell

      The W3C DOM creation method is a valid way of writing code, but it is not the only way. Raw performance speed isn't the primary reason to use a library, and if raw speed is the most important thing to you, then use POJS because when written correctly it will always be faster then a library since you must make a function call to the library.

      That said, your method posted above is, in fact, much slower then the fastest jQuery method I mentioned.

      Looped 100 times -
      FF 3.010 - W3C DOM - 6648 ms vs. jQuery append - 660 ms - DOM Method is 10x slower
      FF 3.1b3 - W3C DOM - 6298 ms vs. jQuery append - 1047 ms - DOM Method is 6x slower
      Webkit - W3C DOM - 1790 ms vs. jQuery append - 285 ms - DOM Method is 6.28x slower

      And your method has all of the W3C DOM speed tricks in it as well, appending to a doc fragment first and creating one node and cloning it.

      Here is the code for the jQuery method, so you can test for yourself:

      var textToInsert = ['<tbody>'];
      var i = 1;
      var length = arr.length;
      for (var a = 0; a < length; a += 1) {
      textToInsert[i++] = '<tr><td>';
      textToInsert[i++] = arr[a];
      textToInsert[i++] = '</td></tr>' ;
      }
      textToInsert.push('</tbody>');
      $('table').append(textToInsert.join(''));

      • mike

        great article,

        it was widely known about array join being faster for ie6, but I would think Jquery would take this approach instead of innerHTML. I am dissapointed the JQuery homesite doesn't cover this issue since by reading the documentation, users would tend to create html by using the append over and over again for each element.

      • RobG

        I was previously using jQuery 1.2.6, using your array version with Safari 4 on Mac OS and jQuery 1.3.2, speed is about the same as the POJS + DOM code that I posted, in Firefox 3 it is about 25% faster.

        Looking at why jQuery is faster, it's because it's adding a new tbody every time. If I do the same with DOM, in Safari it becomes 3 times faster than jQuery, in Firefox it's about the same speed as your jQuery.

        var table = document.getElementsByTagName('table')[0];
        var frag = document.createDocumentFragment();
        var oRow = document.createElement('tr');
        var oCell = document.createElement('td');
        var cell, row;

        for (var i=0, ilen = arr.length; i<ilen; i++) {
        cell = oCell.cloneNode(false);
        cell.appendChild(document.createTextNode(arr[i]));
        row = oRow.cloneNode(false);
        row.appendChild(cell);
        frag.appendChild(row);
        }
        var tbody = document.createElement('tbody');
        tbody.appendChild(frag);
        table.appendChild(tbody);

        The point remains - to get speed, you remove jQuery from the code as much as possible.

        However, if we're really talking about speed, use straight innerHTML:

        var t = ['<table><tbody>'];
        for (var i=0, ilen=arr.length; i<ilen; i++) {
        t.push('<tr><td>' + arr[i]);
        }
        var div = document.createElement('div');
        div.innerHTML = t.join('') + '</table>';
        var table = document.getElementsByTagName('table')[0];
        table.appendChild(div.getElementsByTagName('tbody')[0]);

        That's twice as fast again (i.e. about 5 times faster than your new jQuery hotness) in Safari and makes Firefox twice as fast as your jQuery code. You can keep playing this game, the bottom line is that the less you use jQuery, the faster the code gets.

        Re-loading a local file with jQuery in it is very slow (about 5 seconds in Safari on my MacBook), even if none of its functions are called so even if jQuery was faster (and it isn't), any advantage is more than lost by just having the script in the page.

        (note: edited by article author to fix code that the comment system munged)

      • Josh Powell

        @RobG - If you read above, I am saying that POJS written correctly will always be faster then jquery. Always, because you have to make a function call or access an object attribute . That's why using a simple for loop is faster then using jquery's .each() method. Also jquery's append method has to do cross browser checks in order to abstract away issues like trying to innerHTML a tbody to a table in IE

        In Safari I am getting numbers that are much quicker for your old code then in the other browsers, 305ms vs 6648ms so Safari performs much better at DOM creation then the other browsers, but still negligibly slower then the jquery append method which is 288ms.

        Using your new method of appending the fragment to the tbody and the tbody to the table, I'm actually getting slightly slower results then your previous method, 318ms in safari. It is much quicker then your old method in firefox, taking only 1387 ms vs. 6648ms, but that is still twice as slow as the jquery method of 660ms.

        Using the innerHTML method you posted, I am getting 555ms vs 660ms for the jQuery append in FF 3.01 on a mac, and 224ms vs 228ms in safari.
        In IE7, using vmware, innerHTML gets 1141ms vs 1687ms for the jquery method. Your new DOM node creation method, however, takes 4734ms.

        So, to sum up... innerHTML is generally faster then DOM Node creation, especially in IE. Newer browsers are bridging that gap and DOM methods are becoming much quicker. innerHTML is slightly faster then jQuery append, which makes sense because of the aforementioned reasons and because jquery append has cross browser checks in it and does multiple things. Your point that POJS is faster is well taken, but for me at least, the difference is so minor, that the benefits of library usage far outweigh the slight speed hit. If they don't for you, don't use a library. If they are, however, this article demonstrates the fastest available methods to use jQuery append.

  19. Hey thanks for this. BTW, what tool are you using to get your stats out:

    Ran Once: Profile (107.458ms, 3991 calls, Firefox 3.06)
    Loop of 100: Profile (21641.336ms, 399100 calls)

    Thanks,
    L

  20. janetsmith

    This is an off topic question:

    How do you profile the javascript performance? Especially profiling the performance on different browsers.

    Thanks.

    • I primarily use

      var start = new Date();
      //code to be profiled
      var time = new Date() - start;

      Now, there is talk from John Resig about how some versions of IE measures small amounts of time inaccurately, so this is not completely reliable for testing code cross browser, however the stats for IE vs. IE are still relevant.

      The second method I've used for profiling code is Firebug. There is a "profile" button that moves around based on what version of firebug you are using.

    • Good question.. I was surprised no one ever asked that, especially after it changed. It was the number of milliseconds between the fastest method and the slowest, but that changed after writing the article and faster methods were discovered.

  21. Your article saved me a ton of time trying to trouble shoot an issue and possibly cut my would be code by half. Awesome article. Thanks.

  22. please,
    i'm a little stupid in jquery:

    i have a function with append , i want to know how to stop it, i have used it to creat input element into a div where id is "DivAjAdult", and changing attr id and name from the value of an input where id is "aj_adult" but it doen't stop .. help

    here is the code:

    function addFormField() {
    var id = document.getElementById("aj_adult").value;
    $("#DivAjAdult").append("Nom et prénom d'adulte " + id + "");
    id = (id - 1) + 2;
    document.getElementById("aj_adult").value = id;
    }

  23. re-code:

    function addFormField() {
    var id = document.getElementById("aj_adult").value;
    $("#DivAjAdult").append("Nom et prénom d'adulte " + id + "");
    id = (id - 1) + 2;
    document.getElementById("aj_adult").value = id;
    }

  24. Lawrence Ong

    Thanks for your tip -- it has helped me a lot. I do have a question though.

    First off, I use the jQuery library to design a calendar, the source of which is originally this: http://www.bytecyclist.com/projects/jmonthcalendar/.

    By applying the techiques you proposed, I've been able to display about 500 calendar events in a minute. This was a great improvement since before this (and other enhancements) it would take somewhere in the vicinity of 5 minutes to complete the same task.

    That being said, however, I've hit a wall. for more than a thousand events, it would take ages, and a couple of "script timeout" messages from IE. I do not have the option of switching to another browser as the framework is constrained.

    I would like to display up to 5000 events, but not sure if this technology is even capable of doing this.

    Any suggestions would be appreciated.

    thanks
    Lawrence

  25. John Burgoon

    Thanks so much for this post. Very helpful, almost embarrassing how much faster my looping append is working now. Make the whole string, then append it -- it's almost like chew your food thoroughly THEN swallow it.

    • Josh Powell

      LOL. Great analogy. Also, remember to wrap everything in one element instead of appending a list of elements. See the first comment to this post.

  26. Thanks for your fantastic post .
    I will personally go this way when we really have a long array we have to add the data to the DOM chuck by chuck so it doesn't freezes the page .
    I encourage using setTimeout and having the for loop inside a function .
    we could avoid creating a i variable and doing i++ each time instead we can use array.push

    
          var arr = reallyLongArray,
                    textToInsert =  [''],
                    length = arr.length, 
                    $table = $(table), 
                   chuck = 10
          for (var a = 0; a <length; a += 1) {
              textToInsert.push('');
              textToInsert.push(arr[a]);
              textToInsert.push(' ') ;
             if( a % chuck == 0 ){  
               $table.append(textToInsert.join(''));   
            }
          }
         textToInsert.push('');
        $table.append(textToInsert.join(''));
    
    
    
    • Sorry guys I forgot the & gt;

      
            var arr = reallyLongArray,
                      textToInsert =  ['<tbody>'],
                      length = arr.length,
                      $table = $(table),
                     chuck = 10
            for (var a = 0; a<length; a += 1) {
                textToInsert.push('<tr><td name="pieTD">');
                textToInsert.push(arr[a]);
                textToInsert.push('</td></tr>') ;
               if( a % chuck == 0 ){
                 $table.append(textToInsert.join(''));
              }
            }
           textToInsert.push('</tbody>');
          $table.append(textToInsert.join(''));
      
      
  27. Levi Morrison

    Though dated, this did come up highly ranked in a Google search. Though I only skimmed other posts, it seems no one out-right said it: DOM Manipulation is SLOW! The reason making one big string and appending once is faster has little to do with jQuery performance and lots to do with the fact that DOM operations are slow.

  28. Patrick Fisher

    Appending is far slower than replacing. If you care about speed -- don't append, replace.

    1) Get the old content
    2) Concatenate old + new
    3) Replace old with old + new

    for (var i=0, l=arr.length, a=[]; i<l; i++)
    {
        a.push('<tr><td name="pieTD">' + arr[i] + '</td></tr>');
    }
    var table = $('table');
    table.html(table.html() + a.join(''));

    The get, concatenate and replace are all in the last line. For me, this is almost 4 times as fast as the method given in this article. It is also very readable.

  29. nice job. I had no idea it was so time consuming. Will keep it in mind. Does after() and before() have the same problem? It's being used in this script: http://www.dougnorfolk.com.au/website-design-forster-notes/jquery-add-table-row-with-ajax/

  30. Daniel Henry

    I know this topic is like decades old (lol), but the final example could be made still faster by initializing i to -1 and using pre-increment over post-increment. Post-increments break down into more instructions.

    var arr = reallyLongArray;
    var textToInsert = [];
    var i = -1;
    var length = arr.length;
    for (var a = 0; a < length; ++a) {
        textToInsert[++i]  = '<tr><td name="pieTD">';
        textToInsert[++i] = arr[a];
        textToInsert[++i] = '</td> </tr>' ;
    
    }
    $('table').append(textToInsert.join(''));

15 Pings

  1. [...] 43,439 reasons to use append() correctly - Josh Powell looks at the performance of the jQuery append method, and shows how if used incorrectly it can really ruin your javascript performance. [...]

  2. [...] 43,439 reasons to use append() correctly » Learning jQuery - Tips, Techniques, Tutorials (tags: jquery javascript programming performance) [...]

  3. [...] note - I started off by using the append function inside the $.each loop - but after reading Josh Powell’s post on using append correctly I switched to building the HTML from a concatenated string. The [...]

  4. [...] 43,439 reasons to use append() correctly [...]

  5. [...] 43,439 Reasons to Use append() Correctly: Josh Powell demonstrates how misusing the .append() function in jQuery can dramatically affect performance. [...]

  6. append()를 정확히 써야 하는 43,439가지 이유 | 웹 개발자 아빠의 이야기

    [...] 43,439 reasons to use append() correctly, 2009.03.02, Josh Powell. Posted in Web RSS 2.0 | Trackback | Comment [...]

  7. [...] a particular array all the methods that Prototype adds to their Array and Enumerable objects. 85. 43,439 Reasons to Use Append() Correctly – This technique provides for the proper way of using this jQuery method. Although an extremely [...]

  8. JQuery and Building HTML Elements Fast - ColdfusionPowered

    [...] http://www.learningjquery.com/2009/03/43439-reasons-to-use-append-correctly [...]

  9. jQuery Performance Rules « Dogfeeds——IT Telescope

    [...] For your point #6, there’s a good article explaining that point in details. Go get a look at : http://www.learningjquery.com/2009/03/43439-reasons-to-use-append-correctly [...]

  10. [...] using jQuery for less than a year, but I like it. I also find there is a lot truth to this remark, about the fastest method for turning a string into DOM elements and adding them to the page: I find it curious that developers were drawn to tools like jQuery because they abstracted away [...]

  11. 100 Popular jQuery Examples, Plugins and Tutorials

    [...] a particular array all the methods that Prototype adds to their Array and Enumerable objects. 85. 43,439 Reasons to Use Append() Correctly – This technique provides for the proper way of using this jQuery method. Although an extremely [...]

  12. [...] a particular array all the methods that Prototype adds to their Array and Enumerable objects. 85. 43,439 Reasons to Use Append() Correctly – This technique provides for the proper way of using this jQuery method. Although an extremely [...]

  13. Re : Enhance .append() to add multiple copies at a time - Jquery Home

    [...] $(“#container”).append( html.join(”) ); A good read: http://www.learningjquery.com/2009/03/43439-reasons-to-use-append-correctly [...]

  14. [...] a particular array all the methods that Prototype adds to their Array and Enumerable objects. 85. 43,439 Reasons to Use Append() Correctly – This technique provides for the proper way of using this jQuery method. Although an extremely [...]

  15. [...] a particular array all the methods that Prototype adds to their Array and Enumerable objects. 85. 43,439 Reasons to Use Append() Correctly – This technique provides for the proper way of using this jQuery method. Although an extremely [...]

Sorry, but comments for this entry are now closed.