ptr.childNodes.length = 0;
googling for anything like "remove all child nodes" has everyone
suggesting doing it by removing them one at a time.
Cheers,
--
Tim
"That excessive bail ought not to be required, nor excessive fines imposed,
nor cruel and unusual punishments inflicted" -- Bill of Rights 1689
> To remove all children from an element, does it work just to set the
> childNodes array length to zero, as in:
>
> ptr.childNodes.length = 0;
It shouldn't.
The specs for a nodeList. Not that 'length' is identified as readonly.
http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/
core.html#ID-536297177
> googling for anything like "remove all child nodes" has everyone
> suggesting doing it by removing them one at a time.
I have seen another method that involved doing a shallow clone of the
parent node and replacing it, but then existing references to that node
are now invalid
> To remove all children from an element, does it work just to set the
> childNodes array length to zero, as in:
>
> ptr.childNodes.length = 0;
It shouldn't.
The specs for a nodeList. Note that 'length' is identified as readonly.
http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/
core.html#ID-536297177
> googling for anything like "remove all child nodes" has everyone
> suggesting doing it by removing them one at a time.
I have seen another method that involved doing a shallow clone of the
parent node and replacing it, but then existing references to that node
are now invalid.
> On Fri, 13 Mar 2009 21:47:01 +0000, Tim Streater wrote:
>
> > To remove all children from an element, does it work just to set the
> > childNodes array length to zero, as in:
> >
> > ptr.childNodes.length = 0;
>
> It shouldn't.
>
> The specs for a nodeList. Not that 'length' is identified as readonly.
OK - thanks. Unsurprising really given that all the examples said
otherwise.
> In article <aGAul.1958$im1...@nlpi061.nbdc.sbc.com>,
> Jeremy J Starcher <r3...@yahoo.com> wrote:
>> The specs for a nodeList. Not that 'length' is identified as readonly.
^^^ Should be NOTE.
> OK - thanks. Unsurprising really given that all the examples said
> otherwise.
I tried to cancel this message when I spotted that typo. Ah, well.
Figures.
I think : no
> googling for anything like "remove all child nodes" has everyone
> suggesting doing it by removing them one at a time.
Yes, usually it is something like ;
while(ptr.firstChild) ptr.removeChild(ptr.firstChild)
or
while(ptr.hasChildNode()) ptr.removeChild(ptr.firstChild)
Other way (faster) :
var prt1 = prt.cloneNode(false);
prt.parentNode.replaceChild(prt1, prt);
The usual way is interesting when you need to memorize deleted elements
for re-use (undo ?)
<script type="text/javascript">
var mem = [];
function deletOrUndo(id) {
id = document.getElementById(id);
if(id.getElementsByTagName('*').length>0) {
mem = [];
while(id.firstChild) mem[mem.length] = id.removeChild(id.firstChild);
}
else {
var n = mem.length;
while(n--) id.appendChild(mem[n]);
}
}
</script>
<p><button onclick="deletOrUndo('test')">
empty or fill back 'test'</button></p>
<div id="test">
<h2>test</h2>
<p>line 1</p>
<p>line 2</p>
<p>line 3</p>
</div>
--
sm
else {
for(var i=0, n = mem.length; n>i; i++) id.appendChild(mem[i]);
}
}
Or :
function deletOrUndo(id) {
id = document.getElementById(id);
if(id.getElementsByTagName('*').length>0) {
mem = [];
while(id.lastChild) mem[mem.length] = id.removeChild(id.lastChild);
}
else {
var n = mem.length;
while(n--) id.appendChild(mem[n]);
}
}
--
sm
Why not a simple .innerHTML= ""; ? It's ~ as fast and less cumbersome.
Their relative speeds : http://jorgechamorro.com/cljs/048/
// innerHTML
function innerHTML () {
fillTheContainer();
container.innerHTML= "";
}
// removeChild
function removeChild () {
var e;
fillTheContainer();
while (e= container.firstChild) {
container.removeChild(e);
}
}
// replaceChild
function replaceChild () {
fillTheContainer();
var newE= container.cloneNode(false);
container.parentNode.replaceChild(newE, container);
container= newE;
}
--
Jorge.
Because I use innerHTML least possible ?
> Their relative speeds : http://jorgechamorro.com/cljs/048/
innerHTML 63
replaceChild 70
removeChild 51
Maybe the test would have been better if the 333 inside divs were not
empty ?
They're not being cloned in replaceChild (.cloneNode(false)), and it
takes as long to loop over 333 empty divs as it takes to loop over 333
non-empty divs... Anyway, now there are 666 non-empty <div>(s) and 666
children <p>(s) each containing a text. The results are:
Safari 4.0 on Intel Mac OS X 10_5_6 (debugger off):
innerHTML 664
replaceChild 715
removeChild 92
FF 3.0.7 /Mac (debugger off):
innerHTML 45
replaceChild 62
removeChild 33
Opera 964/Mac (debugger off):
innerHTML 258
replaceChild 232
removeChild 105
Firefox 2.0.0.20 (debugger off):
innerHTML 40
replaceChild 76
removeChild 23
MSIE 6.0 on Windows NT
innerHTML 18
replaceChild 12
removeChild 7
MSIE 7.0 on Windows NT
innerHTML 17
replaceChild 12
removeChild 9
MSIE 8.0 on Windows NT
innerHTML 19
replaceChild 11
removeChild 13
Safari 3.2.2 on Windows NT
innerHTML 352
replaceChild 262
removeChild 242
Chrome 1.0.154.48 on Windows NT
innerHTML 198
replaceChild 166
removeChild 92
Opera 9.64 on Windows NT
innerHTML 109
replaceChild 81
removeChild 67
Firefox 3.0.7 on Windows NT
innerHTML 56
replaceChild 54
removeChild 36
Firefox 3.0.5 on Windows NT
innerHTML 55
replaceChild 38
removeChild 26
You might argue that .innerHTML isn't in the DOM standards (yet), but
XMLHttpRequest isn't either (yet). .innerHTML is a plug to the (highly-
optimized) built-in parser.
Check it out: http://jorgechamorro.com/cljs/048/
var masterContainer= document.createElement('div');
var container= document.body.appendChild(document.createElement
('div'));
var size= 666;
(function fillMasterContainer () {
var n= size;
var txt= "Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Curabitur vitae nibh vitae sem fringilla tempus. Pellentesque id quam
ac lorem porttitor viverra. Curabitur nulla ipsum, mattis quis,
sollicitudin et, pulvinar sit amet, quam. Nunc ultricies risus vel
ipsum. Fusce eleifend urna vestibulum sapien. Praesent vehicula, orci
et mattis laoreet, ipsum enim adipiscing augue, at malesuada dolor
justo non dui. Vivamus aliquet, odio vel ullamcorper viverra, ante
mauris tempor enim, eu mollis orci dui et magna. Etiam non mauris ac
libero consectetur mollis. Pellentesque sed purus. Donec semper
consequat augue. Sed purus erat, luctus imperdiet, porttitor at,
feugiat sed, dui. Mauris vel augue eget metus convallis tempor.
Quisque blandit sem ac magna.";
while (n--) {
masterContainer.appendChild(
document.createElement('div')).appendChild(
document.createElement('p')).innerHTML= txt;
}
})();
function fillTheContainer () {
var newContainer= masterContainer.cloneNode(true);
container.parentNode.replaceChild(newContainer, container);
container= newContainer;
}
--
Jorge.
> Because I use innerHTML least possible ?
Why?
Safari 3.1.1 on iPhone OS 2_2
innerHTML 7
replaceChild 8
removeChild 4
--
Jorge.
Essentialy because :
<http://stephane.moriaux.pagesperso-orange.fr/truc/innerHTML_danger>
--
sm
Dunno what processor you have, but Safari 4 on a 1GHz G4 iBook gives:
innerHTML 118
replaceChild 139
removeChild 25
Anyhow, I think the test is flawed. The browser should be given time
to actually render the content before it is removed - the results for
iPhone are clearly suspicious.
Also, I usually find doing an assignment in the test expression slows
things down, e.g.
while (e= container.firstChild) {
container.removeChild(e);
}
Is faster written as:
while (container.firstChild)
container.removeChild(container.firstChild)
The innerHTML method has issues as it isn't suitable everywhere - I'd
not like to use it on tables in iE. The cloneNode method has issues
too, because the original "container" is replaced (references to the
original are broken and listeners may or may not be still attached
depending on the browser and how they were attached).
Removing the child nodes is more robust. Even though it's the slowest,
it's still reasonably fast for a few hundred elements so if speed
isn't an issue, use it.
It's the old story of optimise when necessary and chose the right tool
for the job.
Below is some alternative test code, on iPhone it gives:
innerHTML: 50
removeChild (assignment outside condition): 106
removeChild (assignment in condition): 115
byClone: 32
Safari 4 on a 1GHz MacBook G4 gives:
innerHTML: 33
removeChild (assignment outside condition): 46
removeChild (assignment in condition): 85
byClone: 4
Firefox 3 on the same MacBook gives:
innerHTML: 37
removeChild (assignment outside condition): 129
removeChild (assignment in condition): 104
byClone: 9
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<title>Remove child nodes test</title>
<body>
<script type="text/javascript">
function iHTML(el) {
el.innerHTML = ''
};
function rChild0(el) {
while (el.firstChild)
el.removeChild(el.firstChild);
}
function rChild1(el) {
var e;
while (e = el.firstChild)
el.removeChild(e);
}
function byClone(el) {
var el2 = el.cloneNode(false);
el.parentNode.replaceChild(el2, el);
}
window.onload = function() {
var msg = [], s, f;
s = new Date();
iHTML(document.getElementById('d0'));
f = new Date();
msg.push('innerHTML: ' + (f - s));
s = new Date();
rChild0(document.getElementById('d1'));
f = new Date();
msg.push('removeChild ' +
'(assignment outside condition): ' + (f - s));
s = new Date();
rChild1(document.getElementById('d2'));
f = new Date();
msg.push('removeChild ' +
'(assignment in condition): ' + (f - s));
s = new Date();
byClone(document.getElementById('d3'));
f = new Date();
msg.push('byClone: ' + (f - s));
alert(msg.join('\n'));
}
</script>
<script type="text/javascript">
var html = [];
var i = 4,
j,
n = 600;
while(i--) {
j = n;
html.push('<div id="d' + i + '">');
while (j--) {
html.push('<p>Ipsum lorem ' + i + '</p>');
}
html.push('</div>');
}
document.write(html.join(''));
</script>
</body>
--
Rob
Yes. Cloning textboxes and tables. Which has nothing at all to do with
*removing* all children.
Because in your tests you count also the display of the 666 divs,
and as I think that falses the results, I made my page :
<http://cjoint.com/data/drac02WPC8_test_delete_elements.htm>
and I get :
Intel Mac OS X 10.4 Firefox/3.0.7 :
100% removeChild = 169
93% innerHTML = 158
2% replaceChild = 3
iCab.4 :
100% removeChild = 3
67% innerHTML = 2
33% replaceChild = 1
Intel Mac OS X Opera/9.63 :
100% removeChild = 7
29% innerHTML = 2
14% replaceChild = 1
Intel Mac OS X 10_4_11 Safari Version/3.1.2 :
100% removeChild = 3
67% innerHTML = 2
67% replaceChild = 2
MSIE 6.0 - Windows XP SP2
83% removeChild = 50
17% innerHTML = 10
100% replaceChild = 60
Except with IE, innerHTML isn't the faster,
and the winer is replaceChild
as if it would be more difficult to write nothing
than to copy-empty-paste-over a div
--
sm
As I told : "essentialy" ...
Anyway, except with IE it seems to me that : innerHTML='';
is not the faster way to remove all those children
<http://cjoint.com/data/drac02WPC8_test_delete_elements.htm>
--
sm
It depends on what the phrase "removing all children of an element"
mean. If the parent "element" in question happens to be a <table> tag
or a <tr> tag then the innerHTML solution may not work on IE.
.innerHTML is always 6.2 times faster... :
"e= container.cloneNode(false); container.parentNode.replaceChild(e,
container); container= e;".length / ",innerHTML= '';".length
:-)
--
Jorge.
Yesterday I (sort-of) fixed that already, because now you see the
results in hz and in ms, and there's a fourth row that times
"fillTeContainer" so that it can be (properly) substracted from the
other results.
The problem I'm having with your tests is that it shows 100% in
removeChild and 0% in the others (on mine and on any other ~fast
machine). That's because, as with RobG's tests, you're attempting to
measure a thing that takes about as much or less than the (1..25 ms)
actual resolution of the timer that you're using to measure it... (!),
and that requires a different strategy.
--
Jorge.
> [...]
> Anyway, except with IE it seems to me that : innerHTML=''; is not the
> faster way to remove all those children
>
> <http://cjoint.com/data/drac02WPC8_test_delete_elements.htm>
Did you try to remove the _last_ child instead of the first? From a
theoretical point of view I would expect it to be faster because the list
of child nodes would only loose its last entry. In contrast removing the
first list entry would require readjusting all the other ones.
But, granted, it would depend on how a certain JavaScript interpreter
actually implemented those linked lists. So it's more out of curiosity
that I'm asking.
--
Matthias
/"\
\ / ASCII RIBBON CAMPAIGN - AGAINST HTML MAIL
X - AGAINST M$ ATTACHMENTS
/ \
Yep, it seems to run slightly faster. (tests updated).
--
Jorge.
What do you mean by "readjusting"?
The children would be kept, at the very least, in a doubly linked list or
some such variant. Removing an item from such a list is not dependent where
that item is in the list.
ISTM that the last item is cheaper to remove than any other in the
list because in that case there's just one pointer that needs to be
updated.
--
Jorge.
With my way to do the tests (time in milli seconds)
as, for instance, in my Safari, the operations run during 1 to 3 ms
... to know which one is faster is of not a big interest.
However, to remove the lastChild seems to be a little bit more difficult
(7 ms instead of 6 ms or 25 <-> 19 ms) then you say the contrary.
Curiously I remade test whith my Firefox freshly opened and I get this
time :
Intel Mac OS X 10.4 - Firefox/3.0.7
100% removeChild = 18 (firstChild) = 23 (lastChild)
61% innerHTML = 11
17% replaceChild = 3
instead of on last time :
100% removeChild = 169
93% innerHTML = 158
2% replaceChild = 3
Something to see with cache ? with CPU ?
Always with my Fx.3 and about removeChild
25ms : while (e = container.lastChild) container.removeChild(e);
19ms : while (e = container.firstChild) container.removeChild(e);
25ms : while (container.hasChildNodes())
container.removeChild(container.lastChild);
17ms : while (container.hasChildNodes())
container.removeChild(container.firstChild);
If the elements to remove are hard coded, the times are larger :
<http://pagesperso-orange.fr/stephane.moriaux/truc/test_delete_html_elements.htm>
(1.5 Mo) (remove = hasChild --> firstChild )
Intel Mac OS X Opera/9.63
100% removeChild = 17
41% innerHTML = 7
24% replaceChild = 4
Intel Mac OS X 10_4_11 Safari/3.1.2
100% removeChild = 58
9% innerHTML = 5
9% replaceChild = 5
Intel Mac OS X 10_4_11 Safari/3.1.2 + cache (after reload)
100% removeChild = 8
63% innerHTML = 5
50% replaceChild = 4
Intel Mac OS X 10.4 Firefox/3.0.7 (cache or no cache but FireBug)
100% removeChild = 48
73% innerHTML = 35
21% replaceChild = 10
--
sm
I see 2 against 4 (assertions ?)
or 2 more (or less) than the other one :-p
--
sm
Don't know how you can count in hz ... ?
(nor what that could mean)
> The problem I'm having with your tests is that it shows 100% in
> removeChild and 0% in the others (on mine and on any other ~fast
> machine). That's because, as with RobG's tests, you're attempting to
> measure a thing that takes about as much or less than the (1..25 ms)
and ?
1 is 1 and 25 is 25, no ?
> actual resolution of the timer that you're using to measure it... (!),
> and that requires a different strategy.
Don't see ...
With your last tests :
<http://homepage.mac.com/jorgechamorro/cljs/048/>
Firefox 3.0.7 on Intel Mac OS X 10.4
innerHTML 220 (4.55ms)
replaceChild 168 (5.95ms)
newContainer 164 (6.1ms)
removeFirstChild 60 (16.67ms)
removeLastChild 56 (17.86ms)
60=16.76 && 56=17.86 ???? inversion of duration ?
The image bellow ... I don't know what it represents (unit of numbers)
- inner (55)
- replace (42)
- remove-first (15)
- remove-last (14)
- new (41)
With Safari, everything except removing is very close
Safari 3.1.2 on Intel Mac OS X 10_4_11
innerHTML 1128 (0.89ms)
replaceChild 1072 (0.93ms)
removeFirstChild 444 (2.25ms)
removeLastChild 440 (2.27ms)
newContainer 1084 (0.92ms)
--
sm
iCab.4 :
inner = 18 ms or 20 ms
Use W3C DOM table methods = 44 ms
the rest close of 27 ms
My Fx.3 :
inner = 41 and 42 ms
the rest = 109 to 122 ms
Globally, anyone is immediat.
Everybody knows that innerHTML is the best way to insert a table.
Just we have to also know what are the limitations of this way to do.
(row, cell ...)
If I had some attributes to each cell
as 'style', 'onclick', 'onmouseover',
My IE6 runs
- inner_1 in 6980 ms
- inner_2 in 210 ms
and Fx runs both in 130 ms
Certainly in these tests the construction of the "string" has something
to do with the resulting speed in IE.
Is that 'string' usually made by JS or serverside ?
--
sm
If I add some attributes !
--
sm
Oh my.
There is only one pointer in the first element as well.
And updating that pointer is one machine instruction, of which your computer
can do three thousand million in a second.
I forgot that e.parentNode has a .lastChild too (I was only thinking
about e.parentNode.firstChild as the start of the linked list). In any
case, when removing an element there are a number of pointers to
adjust (*):
If e is the firstChild: e.nextSibling.previousSibling and
e.parentNode.firstChild.
If e is the lastChild: e.previousSibling.nextSibling and
e.parentNode.lastChild.
If e is the firstChild and the lastChild: e.parentNode.firstChild and
e.parentNode.lastChild
If e is an intermediate: e.previousSibling.nextSibling and
e.nextSibling.previousSibling
In the element that's being removed: e.parentNode= e.nextSibling=
e.previousSibling= null;.
> And updating that pointer is one machine instruction, of which your computer
> can do three thousand million in a second.
Yes, agreed, but *why* does it run faster, then ?
(because it's true that runs faster).
(*)
function removeChild (e) {
if (e.parentNode !== this) {
return; //or throw ?
}
if (e.previousSibling) {
//It's not the firstChild
if (e.previousSibling.nextSibling= e.nextSibling) {
//it's not the lastChild
e.nextSibling.previousSibling= e.previousSibling;
} else {
//it's the lastChild
this.lastChild= e.previousSibling;
}
} else if (e.nextSibling) {
//It's not the lastChild
if (e.nextSibling.previousSibling= e.previousSibling) {
//it's not the firstChild
e.previousSibling.nextSibling= e.nextSibling;
} else {
//it's the firstChild
this.firstChild= e.nextSibling;
}
} else {
//intermediate.
this.firstChild= this.lastChild= null;
}
e.parentNode= e.previousSibling= e.nextSibling= null;
return e;
};
--
Jorge.
No. If you remove the first, the index for each remaining child
element needs to change. If you remove the last, no other element's
index needs to change.
> And updating that pointer is one machine instruction, of which your computer
> can do three thousand million in a second.
Your CPU may process that number of simple instructions per second,
however higher level operations such as removing a node from a
NodeList or updating an index may require tens or hundreds of such
instructions.
--
Rob
You can only know that from the source for a particular browser. I
would not expect browsers to maintain all such references, it is
likely more efficient to just keep the indexes updated and calculate
the other properies as required.
In any case, forming a theory as to which method *might* be the
fastest based on some logical deduction just gives you a theory to
test, nothing more. It's the observed behaviour that must be the
guide.
--
Rob
> Jorge wrote:
>> On Mar 15, 12:57 pm, "rf" <r...@z.invalid> wrote:
>>>
>>> The children would be kept, at the very least, in a doubly linked list
>>> or some such variant. Removing an item from such a list is not
>>> dependent where that item is in the list.
>>
>> ISTM that the last item is cheaper to remove than any other in the list
>> because in that case there's just one pointer that needs to be updated.
>
> Oh my.
>
> There is only one pointer in the first element as well.
You're assuming a certain implementation for what in JavaScript appears
as an array: A double linked list with optimisations for the case in
question. And while that might be true (personally I'd be surprised if
it would be otherwise) it's not the only possible implementation. Just
have a look at C++/STL with all its various types of lists.
> And updating that pointer is one machine instruction, of which your
> computer can do three thousand million in a second.
That's not the complete picture. The pointer about to be replaced must
be copied before in order to release the memory it's pointing to. Just
"updating" it (as you call it) would result in serious memory leaks.
Don't forget that we're not taking about JavaScript here (with its more
or less intelligent garbage collector) but about the level below, i.e.
the way a certain JavaScript interpreter itself might be implemented.
> You're assuming a certain implementation for what in JavaScript
> appears as an array:
I think I have been suitably proven to be somewhat incomplete in my thinking
here :-)