469,271 Members | 997 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 469,271 developers. It's quick & easy.

DOM table building

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?

--
Christopher Benson-Manica | I *should* know what I'm talking about - if I
ataru(at)cyberspace.org | don't, I need to know. Flames welcome.
Jul 23 '05 #1
7 1564
Christopher Benson-Manica wrote:
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>
--
Rob
Jul 23 '05 #2
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
Jul 23 '05 #3
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?
--
Rob
Jul 23 '05 #4
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>

Jul 23 '05 #5
Kiran Makam wrote:
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.

--
Rob
Jul 23 '05 #6
"Andrew Poulos" <ap*****@hotmail.com> wrote in message
news:41***********************@per-qv1-newsreader-01.iinet.net.au...
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.


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.

--
Grant Wagner <gw*****@agricoreunited.com>
comp.lang.javascript FAQ - http://jibbering.com/faq
Jul 23 '05 #7
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
Jul 23 '05 #8

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

4 posts views Thread by brian | last post: by
1 post views Thread by Little | last post: by
4 posts views Thread by rdemyan via AccessMonster.com | last post: by
3 posts views Thread by NullQwerty | last post: by
5 posts views Thread by jrod11 | last post: by
1 post views Thread by CARIGAR | last post: by
reply views Thread by zhoujie | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.