Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
374 views
in Technique[技术] by (71.8m points)

javascript - removing childNodes using node.childNodes.forEach

Traditionally, a suggested way of removing a node's children in Javascript is to do something like this:

while(node.firstChild) {
    node.removeChild(node.firstChild);
}

Recently, I attempted to remove all of a node's children using the built in forEach() method:

node.childNodes.forEach(child => {
    node.removeChild(child);
}

This didn't function as I expected. Instead of removing all child nodes, the forEach stopped executing, leaving nodes leftover. What I ended up having to do was use Array.from:

Array.from(node.childNodes)

And then I could remove nodes with forEach. The reason I could not use the traditional method mentioned above is because for some reason, one child was always left behind, causing an infinite loop.

Why does the childNodes.forEach method not remove all the nodes as I thought it would? What am I misunderstanding?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

node.childNodes is a live collection. As you remove items from it, the collection itself is modified (live while you're iterating). Trying to iterate it as you are, causes elements to be removed from the collection and moved down in the array-like structure while you're iterating it, causing you to miss nodes.

As an example, when you call removeChild() on the 2nd element in the collection, that element itself is then removed from the collection. That causes what was the 3rd element to be moved into the spot in the collection where the 2nd element was. Now, your loop moves on to the 3rd element in the collection. But, that will skip over the element that is now in the 2nd position causing you to never remove it.

That means the only safe way to iterate through the actual collection and remove things is with a backwards traversal because removing things form the end does not cause other elements to change their position in the collection. Removing items from the front (which is what you were doing) does cause items to move in the collection.

Array.from() converts the live collection to a static array where items are not removed from the array while deleting items from the DOM.

I have a personal rule of DOM development to NEVER use a live collection while I'm modifying the DOM in any way because the danger that the live collection gets modified while I'm trying to use it is too high. Array.from() is a very simple way to get a copy of a live collection that's static and is safe to work with, even as the DOM is being modified.


Another safe way to delete them all is with this backwards iteration because items are removed from the end of the live collection which doesn't cause any items to move in the collection that you haven't yet processed:

for (let i = node.childNodes.length - 1; i >= 0; i--) {
   node.removeChild(node.childNodes[i]);
}

But, I generally find this more cumbersome than just converting to a static array with Array.from() as you've already discovered.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...