Gmail Calendar Documents Reader Web more »
Recently Visited Groups | Help | Sign in
Google Groups Home
Optimize large DOM inserts
There are currently too many topics in this group that display first. To make this topic appear first, remove this option from another topic.
There was an error processing your request. Please try again.
flag
  Messages 1 - 25 of 30 - Collapse all  -  Translate all to Translated (View all originals)   Newer >
The group you are posting to is a Usenet group. Messages posted to this group will make your email address visible to anyone on the Internet.
Your reply message has not been sent.
Your post was successful
 
From:
To:
Cc:
Followup To:
Add Cc | Add Followup-to | Edit Subject
Subject:
Validation:
For verification purposes please type the characters you see in the picture below or the numbers you hear by clicking the accessibility icon. Listen and type the numbers you hear
 
James  
View profile  
 More options Feb 5, 2:03 pm
From: James <james.gp....@gmail.com>
Date: Thu, 5 Feb 2009 11:03:28 -0800 (PST)
Local: Thurs, Feb 5 2009 2:03 pm
Subject: Optimize large DOM inserts
I need tips on optimizing a large DOM insert to lessen the "freeze" on
the browser.

Scenario:
I receive a large amount of JSON 'data' through AJAX from a database
(sorted the way I want viewed), and loop through them to add to a JS
string, and insert that chunk of string into a tbody of a table. Then,
I run a plug-in that formats the table (with pagination, etc.).
Simplified sample code:

var html = '';
$.each(data, function(i, row) {
     html += '<tr><td>data from json</td></tr>';

});

$("tbody").append(html);
$("table").formatTable();

formatTable() requires that the table has to be "completed" before it
can be executed.
Is there any way I can optimize this better? I think I've read
somewhere that making a string too long is not good, but I've also
read that updating the DOM on each iteration is even worst.

Any advice would be appreciated!


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
James  
View profile  
 More options Feb 5, 3:56 pm
From: James <james.gp....@gmail.com>
Date: Thu, 5 Feb 2009 12:56:51 -0800 (PST)
Local: Thurs, Feb 5 2009 3:56 pm
Subject: Re: Optimize large DOM inserts
I see. Thanks for the tip. I'll try to work on that!

On Feb 5, 10:38 am, Ricardo Tomasi <ricardob...@gmail.com> wrote:


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Michael Geary  
View profile  
 More options Feb 5, 7:06 pm
From: "Michael Geary" <m...@mg.to>
Date: Thu, 5 Feb 2009 16:06:04 -0800
Local: Thurs, Feb 5 2009 7:06 pm
Subject: RE: [jQuery] Optimize large DOM inserts
Here's a way you can make that much faster:

    function loadTable( rows ) {
        var out = [], o = -1;
        out[++o] = '<table><tbody>';
        for( var row, iRow = -1;  row = rows[++iRow]; ) {
            out[++o] = '<tr><td>';
            out[++o] = row.whatever;
            out[++o] = '</td></tr>';
        }
        out[++o] = '</tbody></table>';
        $('#container').html( out.join('') );
    }

This is faster for several reasons:

* Explicit for loop (and the fastest kind of for loop) - no function
callback for each row.

* Builds an array of string fragments and then joins it, instead of string
concatenation.

* Buils the array with "out[++o] = ..." instead of the more obvious
"out.push(...)" (faster in IE).

* Instead of inserting a large number of sibling DOM elements (the TRs)
together, inserts only a single DOM element (the TABLE).

Note that the for loop will fail if the array may have null/false/zero/empty
string elements. This is usually a safe assumption if each 'row' in the JSON
data is actually an object, e.g.

    json = {
        "rows": [
            { "whatever":"something", "more":"something else" },
            { "whatever":"something 2", "more":"something else 2" },
            { "whatever":"something 3", "more":"something else 3" }
        ]
    };

If, for example, those array entries are strings and empty strings are a
possibility, e.g.:

    json = {
        "rows": [
            "something",
            "",
            "something 3"
        ]
    };

then use a (somewhat) more conventional for loop:

        for( var iRow = -1, nRows = rows.length;  ++iRow < nRows; ) {
            out[++o] = '<tr><td>';
            out[++o] = rows[iRow];
            out[++o] = '</td></tr>';
        }

Let me know if this helps!

-Mike


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Henrik Javén  
View profile  
 More options Feb 5, 6:08 pm
From: Henrik Javén <hja...@gmail.com>
Date: Thu, 5 Feb 2009 15:08:22 -0800 (PST)
Local: Thurs, Feb 5 2009 6:08 pm
Subject: Re: Optimize large DOM inserts
this will be significantly faster

var html = [];
$.each(data, function(i, row) {
     html.push('<tr><td>data from json</td></tr>');

});

$("tbody").append(html.join(''));
$("table").formatTable();

On Feb 5, 12:56 pm, James <james.gp....@gmail.com> wrote:


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Henrik Javén  
View profile  
 More options Feb 5, 5:46 pm
From: Henrik Javén <hja...@gmail.com>
Date: Thu, 5 Feb 2009 14:46:38 -0800 (PST)
Local: Thurs, Feb 5 2009 5:46 pm
Subject: Re: Optimize large DOM inserts
This will be significantly faster

var html = [];
$.each(data, function(i, row) {
     html.push('<tr><td>data from json</td></tr>');

});

$("tbody").append(html.join(''));
$("table").formatTable();

On Feb 5, 12:56 pm, James <james.gp....@gmail.com> wrote:


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Michael Geary  
View profile  
 More options Feb 5, 10:25 pm
From: "Michael Geary" <m...@mg.to>
Date: Thu, 5 Feb 2009 19:25:31 -0800
Local: Thurs, Feb 5 2009 10:25 pm
Subject: RE: [jQuery] Re: Optimize large DOM inserts
"...there is not much room for improvement left."

You just know that when you say that, someone will come along with a 20x-40x
improvement. ;-)

http://mg.to/test/loop1.html

http://mg.to/test/loop2.html

Try them in IE, where the performance is the worst and matters the most.

On my test machine, the first one runs about 6.3 seconds and the second one
about 0.13 seconds.

-Mike


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
jQuery Lover  
View profile  
 More options Feb 6, 3:30 am
From: jQuery Lover <ilovejqu...@gmail.com>
Date: Fri, 6 Feb 2009 13:30:37 +0500
Local: Fri, Feb 6 2009 3:30 am
Subject: Re: [jQuery] Re: Optimize large DOM inserts
That is true....

I always suggest using built in for() loop with huge data sets... and
people keep ignoring me :)))

----
Read jQuery HowTo Resource  -  http://jquery-howto.blogspot.com


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Stephan Veigl  
View profile  
 More options Feb 6, 4:41 am
From: Stephan Veigl <stephan.ve...@gmail.com>
Date: Fri, 6 Feb 2009 10:41:04 +0100
Local: Fri, Feb 6 2009 4:41 am
Subject: Re: [jQuery] Re: Optimize large DOM inserts
Hi,

thanks fort his little optimization tutorial :-)

One question, is there a difference between a loop with a running
variable (e.g. for (i=0; i<rows.length; i++) ) and a for-in loop (e.g.
for (var i in rows) )?

I've done some tests and couldn't find any performance difference
beside the normal variation between two different measurements.
Which variant should be preferred?

by(e)
Stephan

2009/2/6 jQuery Lover <ilovejqu...@gmail.com>:


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Gordon  
View profile  
 More options Feb 6, 4:50 am
From: Gordon <grj.mc...@googlemail.com>
Date: Fri, 6 Feb 2009 01:50:24 -0800 (PST)
Local: Fri, Feb 6 2009 4:50 am
Subject: Re: Optimize large DOM inserts
.each isn't the best choice in this case, a good old-fashioned for
loop will be significantly faster.

Also, use an array of strings instead of a single one and string
concatenation.  Using array.push () is faster than using the +=
operator on a string. Once the loop finishes use join () to get the
array's contents as a single string.

var myArray = [];

for (var i=0; i < data.length; i++)
{
    myArray.push ('<tr><td>data from json</td></tr>');

}

$('tbody').append (myArray.join (''));

On Feb 5, 7:03 pm, James <james.gp....@gmail.com> wrote:


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Javier Martinez  
View profile  
 More options Feb 6, 5:03 am
From: Javier Martinez <ecentin...@gmail.com>
Date: Fri, 6 Feb 2009 11:03:13 +0100
Local: Fri, Feb 6 2009 5:03 am
Subject: Re: [jQuery] Re: Optimize large DOM inserts

And what about?

for (var i = 0, j = data.length, arr = new Array(j); i < j; i++)
    arr[i] = '<tr><td>data from json</td></tr>';
$('tbody').append (myArray.join (''));

2009/2/6 Gordon <grj.mc...@googlemail.com>


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Rick Faircloth  
View profile  
 More options Feb 6, 9:52 am
From: "Rick Faircloth" <R...@WhiteStoneMedia.com>
Date: Fri, 6 Feb 2009 09:52:19 -0500
Local: Fri, Feb 6 2009 9:52 am
Subject: RE: [jQuery] Re: Optimize large DOM inserts
Hi, Mike, et al...

I'm having the same problem loading the DOM with a lot
of generated HTML and I'm very interested in your approach
to generating the code.

I looked over the example you have at ...loop2.html and
have tried to modify it, but I'm afraid there's too much I don't
understand.

My code works, but it's slow when loading the DOM making the
browser to "freeze" and I'm afraid that will confuse the user.

If it's not too much trouble, would you consider modifying my
code below to follow your pattern so I can see if it speeds up
loading my DOM?

I would appreciate it very much, if you have the time or interest.

Rick

Here's my table building code:

function populateDutyTable(response) {

        //First, empty table body of data
        $('#scheduleBody').empty();
        currentDay = "";

        //For each row within the query
        $.each(response.QGETDUTYSCHEDULE.DATA, function(i, row) {

                if (currentDay != row[1]) {
                //Define a variable that will hold the html string
                var myRow1 = '<tr>';

                        currentDay = row[1];

                        myRow1 += '<td class="cell-day">' + row[1] + '</td>';
                        myRow1 += '<td class="cell-date">' + row[2] + '</td>';
                        myRow1 += '<td class="cell-blank" colspan="5">&nbsp;</td>';
                        myRow1 += '</tr>';

                        $('#scheduleBody').append(myRow1);
                }

                var myRow2 = '<tr>';
                        myRow2 += '<td class="cell-blank-day">&nbsp;</td>';
                        myRow2 += '<td class="cell-blank-date">&nbsp;</td>';
                        myRow2 += '<td class="cell-am-am">' + row[3] + '</td>';
                        myRow2 += '<td class="cell-position">' + row[4] + '</td>';
                        myRow2 += '<td colspan="3">Cell Content</td>';

                        myRow2 += '</tr>';

                        //Append row with names to the body of the table
                        $('#scheduleBody').append(myRow2);

        });

}

And here's what you coded:

                $(function() {

                        var rows = [];
                        for( var i = 0;  i < 2000;  ++i ) {
                                rows[i] = { text: ''+i };
                        }

                        var t1 = + new Date;

                        function loadTable( rows ) {
                                var out = [], o = -1;
                                out[++o] = '<table><tbody>';
                                for( var row, iRow = -1;  row = rows[++iRow]; ) {
                                        out[++o] = '<tr><td>';
                                        out[++o] = row.text;
                                        out[++o] = '</td></tr>';
                                }
                                out[++o] = '</tbody></table>';
                                $('#container').html( out.join('') );
                        }
                        loadTable( rows );

                        var t2 = + new Date;
                        alert( ( t2 - t1 ) / 1000 + ' seconds' );
                });


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Ricardo Tomasi  
View profile  
 More options Feb 6, 10:45 am
From: Ricardo Tomasi <ricardob...@gmail.com>
Date: Fri, 6 Feb 2009 07:45:19 -0800 (PST)
Local: Fri, Feb 6 2009 10:45 am
Subject: Re: Optimize large DOM inserts
Hi Michael,

Ouch. That's quite a difference. I thought about the join vs
concatenate performance difference in IE, but didn't remember it could
be that large. I take that one, I was being displiscent.

cheers,
- ricardo

On Feb 6, 1:25 am, "Michael Geary" <m...@mg.to> wrote:


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
James  
View profile  
 More options Feb 6, 3:28 pm
From: James <james.gp....@gmail.com>
Date: Fri, 6 Feb 2009 12:28:15 -0800 (PST)
Local: Fri, Feb 6 2009 3:28 pm
Subject: Re: Optimize large DOM inserts
Big thanks for the optimization!
It certainly did optimize the loop processing by several folds!

However, for my case, I found that the ultimate bottleneck was the
plug-in function that I was using that froze the browser the longest.
The actual insert to the DOM took a bit of time also and did freeze
the browser, but wasn't too bad even in IE6.

By the way, do you have any tips on emptying a large amount of content
in the DOM?
Such as emptying that whole chunk of HTML that was inserted. That also
freezes the browser also.
I'm currently using $(el).empty(); and not sure if there's a more
optimal solution.
Thanks!

On Feb 5, 5:25 pm, "Michael Geary" <m...@mg.to> wrote:


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Ricardo Tomasi  
View profile  
(1 user)  More options Feb 6, 4:48 pm
From: Ricardo Tomasi <ricardob...@gmail.com>
Date: Fri, 6 Feb 2009 13:48:33 -0800 (PST)
Local: Fri, Feb 6 2009 4:48 pm
Subject: Re: Optimize large DOM inserts
Now I might have something useful to say! :)

I remember this being discussed long ago on the 'replaceHTML' subject.
Apparently using .innerHTML on an element that is not in the DOM is
much faster (except for IE which is already fast). See here:

http://blog.stevenlevithan.com/archives/faster-than-innerhtml
http://www.bigdumbdev.com/2007/09/replacehtml-remove-insert-put-back-...

cheers,
- ricardo

On Feb 6, 6:28 pm, James <james.gp....@gmail.com> wrote:


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
James  
View profile  
 More options Feb 6, 6:06 pm
From: James <james.gp....@gmail.com>
Date: Fri, 6 Feb 2009 15:06:25 -0800 (PST)
Local: Fri, Feb 6 2009 6:06 pm
Subject: Re: Optimize large DOM inserts
Wow! Wow! Wow! Using that replaceHTML function to empty out the
element took practically no time at all!
Though I wasn't able to get it to work properly in IE (tried only IE6)
as oldEl.innerHTML = html; kept bombing on me with "unknown runtime
error". I've removed the IE-only part and let it run the rest. It's
still many times faster than using $(el).empty();

However, I wasn't able to get replaceHTML to work on inserting HTML
into the DOM though. Using the previous suggestions, it would become:
replaceHtml('myElementID', out.join(''));

but the inserted HTML in 'myElementID' had all of the HTML tags (<tr>,
<td>, etc.) stripped out for some reason.

On Feb 6, 11:48 am, Ricardo Tomasi <ricardob...@gmail.com> wrote:


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
James  
View profile  
 More options Feb 6, 6:17 pm
From: James <james.gp....@gmail.com>
Date: Fri, 6 Feb 2009 15:17:13 -0800 (PST)
Local: Fri, Feb 6 2009 6:17 pm
Subject: Re: Optimize large DOM inserts
I just noticed in IE6 that using the replaceHTML to clear the DOM with
events attached definitely creates memory leak. FF seems to clean it
up though.

On Feb 6, 1:06 pm, James <james.gp....@gmail.com> wrote:


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Stephan Veigl  
View profile  
 More options Feb 6, 6:31 pm
From: Stephan Veigl <stephan.ve...@gmail.com>
Date: Sat, 7 Feb 2009 00:31:17 +0100
Local: Fri, Feb 6 2009 6:31 pm
Subject: Re: [jQuery] Re: Optimize large DOM inserts
Hi James,

I run into a similar problem. Replacing parts of tables does not work with IE.
(see http://de.selfhtml.org/javascript/objekte/all.htm#inner_html,
sorry available in German only)
Now I simply replacing the whole table which is still much faster than
my previous version.

by(e)
Stephan

2009/2/7 James <james.gp....@gmail.com>:


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Ricardo Tomasi  
View profile  
(1 user)  More options Feb 7, 1:35 pm
From: Ricardo Tomasi <ricardob...@gmail.com>
Date: Sat, 7 Feb 2009 10:35:10 -0800 (PST)
Local: Sat, Feb 7 2009 1:35 pm
Subject: Re: Optimize large DOM inserts
There's no need to use that function to insert the HTML, as the
performance improvement comes mostly from emptying the element outside
the DOM.

To avoid the memory leak, you'd have to remove all the event listeners
before destroying it - that's exactly what empty() does (+ clearing
any data), and the source of most of it's overhead. You can probably
get around that by not attaching any events to the inner elements and
use event delegation on the <table> or live() instead.

You might get away with something like this, not sure if it will
perform any better:

var c = $('#container')[0],
parent = c.parentNode,
next = c.nextSibling,
old = parent.removeChild(c);

c = old.cloneNode(false).innerHTML = out.join('');
if (next)
   next.insertBefore(c);
else
   parent.appendChild(c);

setTimeout(function(){ $(old).remove(); }, 200); //give the browser
some breathing time then clean up events&data from the old element

The cleaning up part will be unnecessary if you use event delegation.

- ricardo

On Feb 6, 9:17 pm, James <james.gp....@gmail.com> wrote:


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Kevin Dalman  
View profile  
 More options Feb 7, 1:59 pm
From: Kevin Dalman <kevin.dal...@gmail.com>
Date: Sat, 7 Feb 2009 10:59:13 -0800 (PST)
Local: Sat, Feb 7 2009 1:59 pm
Subject: Re: Optimize large DOM inserts
These test pages DO NOT accurately compare the speed of array.join("")
VS string+="...". The biggest speed difference between the two is
REALLY that one uses $("<table></table>") and one doesn't. If this is
removed from the 'slow' page, the load-speed goes from 8.7 sec to 1.5
sec. This is still 6-times longer than the fast version - but no
longer 30-times longer!

Conversely, I changed the 'fast' page to use the append syntax (after
removing <table> and <tbody> from the array):

$('#container').append( $('<table></table>').append( out.join('') ) );

This changed the 'fast' load from 0.24 sec to 7.3 sec! So even the
'fast loop' page performed 30-times slower when the element-append
method is used!

There was another code difference as well - the fast page uses $
('#container').html() and the slow-page $('#container').append().
However, since the container is empty, it does not cause any
significant speed difference.

So for a TRUE loop-speed comparison, the code for the 'slow version'
should be:

var html = '<table><tbody>';
$.each( rows, function( iRow, row ) {
   html += '<tr><td>' + row.text + '</td></tr>';

});

html += '</tbody></table>';
$('#container').html( html );

This now uses the same html() syntax as the fast version, isolating
the difference between the pages to ONLY the 2 loops. Now there is
less than a 1.3 sec difference - instead of 8+ seconds.

Now it becomes clear that the biggest lesson here is that append
("<table<</table>") is a much bigger problem than the loop code. This
is important to know because it would apply even if there were NO loop
at all!

Thanks for bringing both these details to my attention. I do a lot of
dynamic HTML generation, so it's helpful to know what to watch out
for.

/Kevin

On Feb 5, 7:25 pm, "Michael Geary" <m...@mg.to> wrote:


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Michael Geary  
View profile  
 More options Feb 7, 4:41 pm
From: "Michael Geary" <m...@mg.to>
Date: Sat, 7 Feb 2009 13:41:03 -0800
Local: Sat, Feb 7 2009 4:41 pm
Subject: RE: [jQuery] Re: Optimize large DOM inserts
No need to shout. :-)

I never claimed that these pages reflect only the difference between the
array join and string concatenation. On the contrary, in my other message in
this thread I listed all of the factors that make the code faster:

* Explicit for loop (and the fastest kind of for loop) - no function
callback for each row.

* Builds an array of string fragments and then joins it, instead of string
concatenation.

* Builds the array with "out[++o] = ..." instead of the more obvious
"out.push(...)" (faster in IE).

* Instead of inserting a large number of sibling DOM elements (the TRs)
together, inserts only a single DOM element (the TABLE).

Now it is very useful to know how much of the performance improvement is due
to each of these individual tricks, and thank you for doing that testing. My
purpose in posting was to combine all of the optimizations to create the
fastest possible code.

-Mike


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Michael Geary  
View profile  
 More options Feb 7, 5:17 pm
From: "Michael Geary" <m...@mg.to>
Date: Sat, 7 Feb 2009 14:17:05 -0800
Local: Sat, Feb 7 2009 5:17 pm
Subject: RE: [jQuery] Re: Optimize large DOM inserts
Rick, first you need to arrange your HTML to allow you to insert the entire
<table> element in one fell swoop. IOW, put the table inside another
container element that has nothing else in it, e.g.

    <div id="scheduleContainer">
        ... <table> and children will be inserted here ...
    </div>

Then your code might look something like:

    function populateDutyTable(response) {

        var currentDay = '';
        var rows = response.QGETDUTYSCHEDULE.DATA;
        var out = [], o = -1;

        out[++o] = '<table id="scheduleBody">';

        for( var row, i = -1;  row = rows[++i]; ) {

            var day = row[1];
            if( currentDay != day ) {
                currentDay = day;
                out[++o] = '<tr><td class="cell-day">';
                out[++o] = row[1];
                out[++o] = '</td><td class="cell-date">';
                out[++o] = row[2];
                out[++o] = '</td><td class="cell-blank"
colspan="5">&nbsp;</td></tr>';
            }

            out[++o] = '<tr><td class="cell-blank-day">&nbsp;</td><td
class="cell-blank-date">&nbsp;</td><td class="cell-am-am">';
            out[++o] = row[3];
            out[++o] = '</td><td class="cell-position">';
            out[++o] = row[4];
            out[++o] = '</td><td colspan="3">Cell Content</td></tr>';
        }

        out[++o] = '</table>';

        $('#scheduleContainer').html( out.join('') );
    }

In addition to the other optimizations I mentioned in my earlier message, I
also combined consecutive string constants into one. You may prefer to keep
them separate for readability and maintainability, at a slight cost in
speed.

-Mike


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Kevin Dalman  
View profile  
 More options Feb 7, 5:44 pm
From: Kevin Dalman <kevin.dal...@gmail.com>
Date: Sat, 7 Feb 2009 14:44:43 -0800 (PST)
Local: Sat, Feb 7 2009 5:44 pm
Subject: Re: Optimize large DOM inserts
Mike, sorry if my post sounded like criticism - it was not intended
that way. However the representation of the test pages is misleading -
by accident I'm sure.

The majority of the performance difference between the 2 pages is
*solely* the difference between these 2 syntax....

$("#container).append(
   $("<table></table>").append(
      "<tr><td>Hello</td></tr>"
   )
);

$("#container).append(
   "<table><tr><td>Hello</td></tr></table>"
);

Or alternately...

$("#container).html(
   "<table><tr><td>Hello</td></tr></table>"
);

The syntax differences above are responsible for 85% of the speed
difference between the pages - 7-times more than the loop-optimization
is. Yet none of the points you repeated above are related to this
issue. BOTH syntax only do a single DOM append - they just do it
differently. And the 'loop' is irrelevant to this 85% difference - a
simple string will still produce the same result.

If you replace the code in your 'slow page' with the sample I gave in
my last post - using .html( html ) - you'll see that the speed
improves 7-fold -- with no change to the string-concatenation loop or
anything else. Since this is 85% of the speed difference, this is
proper way to comparison of the issues you raised. (You could also use
$("<table></table>") in BOTH pages, but then the 'fast page' takes
over 7 seconds!)

So the loop-optimization  produces only a 6-times speed improvement in
IE - not 30-times. This.is still a significant improvement by itself,
but I think it important to understand that $("<table></table>") is
*really* the big culprit here. In fact, I think this is something that
should be brought to the jQuery team's attention... Why is this syntax
so slower?

Ciao,

/Kevin

On Feb 7, 1:41 pm, "Michael Geary" <m...@mg.to> wrote:


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Rick Faircloth  
View profile  
 More options Feb 7, 6:20 pm
From: "Rick Faircloth" <R...@WhiteStoneMedia.com>
Date: Sat, 7 Feb 2009 18:20:06 -0500
Local: Sat, Feb 7 2009 6:20 pm
Subject: RE: [jQuery] Re: Optimize large DOM inserts
Hey, thanks Michael for taking the time to provide the
explanation and the re-write.  I'll put this into place
and see how it performs.  I'm sure it'll be *much* better!

Rick


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Kevin Dalman  
View profile  
(1 user)  More options Feb 8, 1:51 pm
From: Kevin Dalman <kevin.dal...@gmail.com>
Date: Sun, 8 Feb 2009 10:51:10 -0800 (PST)
Local: Sun, Feb 8 2009 1:51 pm
Subject: Re: Optimize large DOM inserts
I identified why the $("<table />").append syntax is so extremely slow
in the sample page. It actually has nothing to do with being a 'second
DOM append' as was assumed. Here is what I learned from my testing...

This is the HTML mark-up for the tests:

<BODY>
        <DIV id="container"></DIV>
        <TABLE id="Table"></TABLE>
</BODY>

I generated the same 'html' var from Mike's test page - containing
2000 table-rows. The loop used is irrelevant to these tests because
the times shown below are for the DOM insertion *only*.

First, here is the original append-code from Mike's sample page...

$('#container').append(
   $('<table><tbody></tbody></table>').append(html)
);
// run-time: 7.5 sec

Note the extreme slowness of the insertion - over 7 seconds!

But by just *moving* the <tbody> tags, it becomes 20-times faster...

$('#container').append(
   $('<table></table>').append('<tbody>'+ html +'</tbody>')
);
// run-time: 0.36 sec

If we reduce it to a single command, it becomes 35-times faster...

$('#container').append(
    '<table><tbody>'+ html +'</tbody></table>'
);
// run-time: 0.24 sec

BTW, the speed is the same whether .html() or .append() is used.

If the existing table is used instead, we gain only a few ms.

$('#Table').append('<tbody>'+ html +'</tbody>');
// run-time: 0.22 sec

To see if it makes a difference appending to a table that's 'not
empty', I appended the same 2000 rows *10-times* (to keep the math
simple)

html = '<tbody>'+ html +'</tbody>';
$('#Table')
    .append( html )
    .append( html )
    .append( html )
    .append( html )
    .append( html )
    .append( html )
    .append( html )
    .append( html )
    .append( html )
    .append( html )
;
// run-time: 2.23 sec

The run-time to append 10-times is *exactly* 10-times as long as
appending the first time. This means it makes no difference whether
the table is 'empty' or already has content.

So, in my tests, there is *no benefit* to appending an entire table to
an empty container. You get identical performance (in IE7) by
appending the new rows to a table - as long as they are wrapped in a
tbody. If the rows are not inside a tbody, then they are appended one-
by-one! This is why the syntax used by Mike was so extremely slow. It
actually had nothing to do with appending 2 elements, which I knew
could not account for a 3000% difference!

I dynamically generate table rows A LOT. My current web-app appends a
7-row tbody as a 'new record'. But I do not use loops to generate
html. Instead I use a hidden 'template tbody' that I clone. This has
all the events for the form-fields pre-attached. All I do is rename
all the fields after cloning the template, and before appending it to
the target table. This method is much easier to read and update than a
hundred lines of html-generating script. Plus I actually use the same
mark-up to generate the existing records onLoad (via JSP), so there is
zero code duplication.

I wanted to identify the cause of the slow append in Mike's sample
because I never see this in my applications. Now that I understand
what was happening, I see that appending a tbody is actually extremely
efficient - in fact, even faster than writing a new table! This is
good news because I cannot regenerate the entire table each time - I
must append new rows/records to an existing table.

I thought I'd share these results because appending a tbody (or a
single row) provides many more options than writing an entire table.
And now I know there is no performance difference - at least not in
IE7.

Thanks to Mike for providing a starting points for these tests, in
addition to his loop-optimization tips.

/Kevin


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Kevin Dalman  
View profile  
 More options Feb 8, 2:20 pm
From: Kevin Dalman <kevin.dal...@gmail.com>
Date: Sun, 8 Feb 2009 11:20:35 -0800 (PST)
Local: Sun, Feb 8 2009 2:20 pm
Subject: Re: Optimize large DOM inserts
Rick, based on what I've learned from testing, you have another option
now...

Here is a modified version of Mike's code - without generating the
table.

    function populateDutyTable(response) {

        var currentDay = '';
        var rows = response.QGETDUTYSCHEDULE.DATA;
        var out = [], o = -1;

        out[++o] = '<tbody>'; // CHANGED

        for( var row, i = -1;  row = rows[++i]; ) {

            var day = row[1];
            if( currentDay != day ) {
                currentDay = day;
                out[++o] = '<tr><td class="cell-day">';
                out[++o] = row[1];
                out[++o] = '</td><td class="cell-date">';
                out[++o] = row[2];
                out[++o] = '</td><td class="cell-blank"
colspan="5">&nbsp;</td></tr>';
            }

            out[++o] = '<tr><td class="cell-blank-day">&nbsp;</td><td
class="cell-blank-date">&nbsp;</td><td class="cell-am-am">';
            out[++o] = row[3];
            out[++o] = '</td><td class="cell-position">';
            out[++o] = row[4];
            out[++o] = '</td><td colspan="3">Cell Content</td></tr>';
        }

        out[++o] = '</tbody>'; // CHANGED

        $('#scheduleBody').append( out.join('') ); // CHANGED
    }

A container around the table is no longer required because wrapping
the rows in a tbody achieves the same performance as wrapping them in
a table. Plus, you could now add rows without regenerating the entire
table. This provides more options with no penalty. For example, now
you could hard-code the table with column headers - for example...

<table id="scheduleBody">
   <thead>
      <tr>
         <th>ID</th>
         <th>Day</th>
         <th>Date</th>
         <th>Name</th>
      </tr>
   </thead>
</table>

This is cleaner and faster than adding column headers inside your
Javascript loop.

I suggest you try both methods, Rick. Use a timer (like MIike's sample
pages) to confirm whether both are equally fast. Based on my tests,
you may find appending to the table even faster, with cleaner markup
as a bonus.

Ciao,

/Kevin

On Feb 7, 3:20 pm, "Rick Faircloth" <R...@WhiteStoneMedia.com> wrote:


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Messages 1 - 25 of 30   Newer >
« Back to Discussions « Newer topic     Older topic »

Create a group - Google Groups - Google Home - Terms of Service - Privacy Policy
©2009 Google