DOM table building

  • Thread starter Christopher Benson-Manica
  • Start date
C

Christopher Benson-Manica

Why is building a table with the DOM slower than using an array? IOW,
why is

var table=document.createDocumentFragment();
for( var i=0; i < 4000; i++ ) {
tr=table.insertRow( table.rows.length );
td=tr.appendChild( document.createElement('td') );
td.appendChild( 'foo' );
}
someElement.appendChild( table );

much slower than

var table[];
table.push( "<table>" );
for( var i=0; i < 4000; i++ ) {
table.push( "<tr>" );
table.push( "<td>foo</td>" );
table.push( "</tr>" );
}
table.push( "</table>" );
document.writeln( table.join('') ); // thanks again for the tip!!

? Am I doing something inadvisable with the document object?
 
R

RobG

Christopher said:
Why is building a table with the DOM slower than using an array? IOW,
why is

It depends on the browser. In Safari, DOM is faster (or innerHTML is
more sluggish...)
var table=document.createDocumentFragment();
for( var i=0; i < 4000; i++ ) {
tr=table.insertRow( table.rows.length );

This is faster as:

tr=table.insertRow(i);
td=tr.appendChild( document.createElement('td') );
td.appendChild( 'foo' );

This line created an error in Firefox and IE, use:

td.appendChild(document.createTextNode('foo'));

[...]
? Am I doing something inadvisable with the document object?

Only in regard to unnecessarily calculating the number of rows on each
iteration - just use the counter, "i".

My results (times approx in milliseconds):

Browser DOM innerHTML
Firefox 4,200 350
IE 40,000 200

I could not believe the IE time, so I ran the test several times (code
below)

From the results I surmise that Microsoft has no interest in making DOM
methods more efficient. This is a rather narrow minded view, since in
IE you can't build part of a table using innerHTML, you must build all
or nothing. So if you want to replace a row, you must use their
sluggish DOM method - which Firefox will run 10 times faster.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>demo</title>
<script type="text/javascript">
function doTable(){
var now = new Date();
var table = document.createElement('table');
table.style.border = "1px solid blue";
for( var i=0; i < 4000; i++ ) {
tr=table.insertRow(i);
td=tr.appendChild( document.createElement('td') );
td.appendChild( document.createTextNode('foo') );
}

document.getElementById('dom').appendChild( table );

var now2 = new Date();
var x = now2.getTime() - now.getTime();
var msg = 'DOM way: ' + x;

var now = new Date();
var table = [
"<table style='border: 1px",
" solid red;'>",
"<tbody>"
];
for( var i=0; i < 4000; i++ ) {
table.push( "<tr><td>foo</td></tr>" );
}
table.push( "</tbody></table>" );

document.getElementById('inner').innerHTML = table.join('');
var now2 = new Date();
var x = now2.getTime()-now.getTime()
msg += '\ninnerHTML way: ' + x;
alert(msg);
}
</script>
</head>
<body onload="doTable();">
<div id="dom"></div>
<div id="inner"></div>
</body>
</html>
 
A

Andrew Poulos

RobG wrote:
[snip]
My results (times approx in milliseconds):

Browser DOM innerHTML
Firefox 4,200 350
IE 40,000 200

I could not believe the IE time, so I ran the test several times (code
below)

From the results I surmise that Microsoft has no interest in making DOM
methods more efficient. This is a rather narrow minded view, since in
IE you can't build part of a table using innerHTML, you must build all
or nothing. So if you want to replace a row, you must use their
sluggish DOM method - which Firefox will run 10 times faster.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>demo</title>
<script type="text/javascript">
function doTable(){
var now = new Date();
var table = document.createElement('table');
table.style.border = "1px solid blue";
for( var i=0; i < 4000; i++ ) {
tr=table.insertRow(i);
td=tr.appendChild( document.createElement('td') );
td.appendChild( document.createTextNode('foo') );
}

document.getElementById('dom').appendChild( table );

var now2 = new Date();
var x = now2.getTime() - now.getTime();
var msg = 'DOM way: ' + x;

var now = new Date();
var table = [
"<table style='border: 1px",
" solid red;'>",
"<tbody>"
];
for( var i=0; i < 4000; i++ ) {
table.push( "<tr><td>foo</td></tr>" );
}
table.push( "</tbody></table>" );

document.getElementById('inner').innerHTML = table.join('');
var now2 = new Date();
var x = now2.getTime()-now.getTime()
msg += '\ninnerHTML way: ' + x;
alert(msg);
}
</script>
</head>
<body onload="doTable();">
<div id="dom"></div>
<div id="inner"></div>
</body>
</html>
I edited your code to this:


var table = document.createElement('table');
table.style.border = "1px solid blue";
var b = document.createElement("tbody");

for( var i = 0; i < 4000; i++ ) {
tr = document.createElement("tr");
td = tr.appendChild( document.createElement('td') );
td.appendChild( document.createTextNode('foo') );
b.appendChild(tr);
}

table.appendChild(b);
document.getElementById('dom').appendChild( table );


And got the DOM response from IE6 on WinXP SP2 to under 1,000ms. The
innerHTML response was under 200ms.

Andrew Poulos
 
R

RobG

Andrew Poulos wrote:
[...]
var table = document.createElement('table');
table.style.border = "1px solid blue";
var b = document.createElement("tbody");

for( var i = 0; i < 4000; i++ ) {
tr = document.createElement("tr");
td = tr.appendChild( document.createElement('td') );

I guess strictly speaking this should be:

var tr = document.createElement('tr');
var td = tr.appendChild( document.createElement('td') );

but reducing the scope to local makes no difference to the speed that I
can tell.
td.appendChild( document.createTextNode('foo') );
b.appendChild(tr);
}

table.appendChild(b);
document.getElementById('dom').appendChild( table );


And got the DOM response from IE6 on WinXP SP2 to under 1,000ms. The
innerHTML response was under 200ms.

Very similar results here (Firefox is still twice as fast as IE).

It seems explicitly creating the tbody, rather than allowing the
browser to create it by inference, saves a lot of time. I can only
guess that the modifications required to the tbody each time the tr is
added to the table directly takes much more effort than adding to the
tbody, then (ultimately) to the table. But four times the effort for
Firefox or 20 times the effort for IE seems extreeme when most
programmers don't put tbodys in their tables (yeah, I know the spec
says they should be there, maybe browsers are too tolerant).

Anyone else with an optimisation?
 
K

Kiran Makam

I applied Duff's Device ( used his technique instead of normal for
loop,
more info available at
http://homepage.mac.com/rue/JS_Optimization_Techniques/ )

Results were amazing in IE:
Normal for loop:1235 milli secs
Duff's Device: 156 milli secs

Here is the code:

<div id=dom></div>
<script>
var start = new Date();

var table = document.createElement('table');
table.style.border = "1px solid blue";
var b = document.createElement("tbody");

var iterations = 4000
var testVal=0;
var n = iterations % 8;

if (n>0) {
do
{
testVal++;
}
while (--n); // n must be greater than 0 here
}

n = parseInt(iterations / 8);
do
{
tr = document.createElement("tr");
td = tr.appendChild( document.createElement('td') );
td.appendChild( document.createTextNode('foo') );
b.appendChild(tr);
}
while (--n);


table.appendChild(b);
document.getElementById('dom').appendChild( table );
var end = new Date();
document.write( (end - start) );
</script>
 
R

RobG

Kiran said:
I applied Duff's Device ( used his technique instead of normal for
loop,
more info available at
http://homepage.mac.com/rue/JS_Optimization_Techniques/ )

Results were amazing in IE:
Normal for loop:1235 milli secs
Duff's Device: 156 milli secs

I'm not surprised. Let's see, 1235/156 = 7.9 ... pretty close to 8.
Here is the code:

<div id=dom></div>
<script>
var start = new Date();

var table = document.createElement('table');
table.style.border = "1px solid blue";
var b = document.createElement("tbody");

var iterations = 4000
var testVal=0;
var n = iterations % 8;

Now why would I want iterations mod 8?
if (n>0) {
do
{
testVal++;
}
while (--n); // n must be greater than 0 here
}

n = parseInt(iterations / 8);

More to the point, n is now iterations *divided* by 8...
do
{
tr = document.createElement("tr");
td = tr.appendChild( document.createElement('td') );
td.appendChild( document.createTextNode('foo') );
b.appendChild(tr);
}
while (--n);

So it only iterates 4000/8 = 500 times.

modify the createTextNode line to be:

td.appendChild( document.createTextNode('foo ' + ' : ' + n));

and you will see that only 500 cells/rows are written to the table,
hence the magical 8 fold increase in speed. More remarkable is that
the reduction can be entire blamed on the reduction in the number of
elements created.

QED

Sorry to burst your bubble.
 
A

Andrew Poulos

Grant Wagner wrote:

[snip]
If you really are creating a table with one (or even many) columns and
they all contain the same text, then it takes about 1/2 the time to
execute:

var tr = document.createElement("tr");
var td = tr.appendChild(document.createElement('td'));
td.appendChild(document.createTextNode('foo'));
for (var i = 0; i < 4000; i++) {
b.appendChild(tr.cloneNode(true));
}

Create the row and the cell(s) once, then cloneNode() them. I'd imagine
this would actually get faster the more you called
document.createElement()/createTextNode() inside the loop (ie - tables
with more columns would benefit more from this algorithm).

Certainly cloneNode() is just walking the DOM hierarchy of the node
you're cloning and doing what you would do anyway, but it's under the
control of the script engine, not your script, so it should run faster
(the same way Array#join() is faster than manually joining an Array).

Of course I'm not sure how realistic it is to assume you'd be creating a
4000 row table with all rows identical.

Who knows though. It might be faster to create a 4000 row x 20 column
table using cloneNode(), then come back and change the nodeValues to
what they are supposed to be. You'd have to test it.

I tested creating the table using cloneNode and averaged these values
(in milliseconds):

Browser DOM innerHTML
Firefox 430 280
IE 625 150

Andrew Poulos
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top