传统上,在Javascript中删除节点子节点的建议方法是执行以下操作:
while(node.firstChild) {
node.removeChild(node.firstChild);
}
最近,我尝试使用内置的forEach()方法删除所有节点的子节点:
node.childNodes.forEach(child => {
node.removeChild(child);
}
这并没有像我预期的那样发挥作用。 forEach停止执行,而不是删除所有子节点,而是留下剩余的节点。我最终要做的是使用Array.from:
Array.from(node.childNodes)
然后我可以用forEach删除节点。我不能使用上面提到的传统方法的原因是因为某些原因,一个孩子总是被遗忘,导致无限循环。
为什么childNodes.forEach方法没有像我想象的那样删除所有节点?我误解了什么?
答案 0 :(得分:7)
node.childNodes
是一个真人秀。当您从中删除项目时,集合本身会被修改(在您重复迭代时生效)。尝试按原样迭代它,导致元素从集合中移除,并在您重复迭代时向下移动到类似数组的结构中,导致您错过节点。
例如,当您在集合中的第二个元素上调用removeChild()
时,该元素本身将从集合中删除。这导致第3个元素移动到第2个元素所在的集合中的位置。现在,您的循环移动到集合中的第3个元素。但是,这将跳过现在处于第二位置的元素,导致您永远不会删除它。
这意味着迭代实际集合和删除事物的唯一安全方法是向后遍历,因为从结尾删除事物不会导致其他元素更改其在集合中的位置。从正面删除项目(这正是您正在做的事情)确实会导致项目在集合中移动。
Array.from()
将实时集合转换为静态数组,其中从DOM中删除项目时不会从数组中删除项目。
我有一个DOM开发的个人规则,在我以任何方式修改DOM时都不会使用实时收藏,因为在我尝试使用它时,实时收藏被修改的危险性也是如此高。 Array.from()
是一种非常简单的方法,可以获取静态且可以安全使用的实时集合的副本,即使在修改DOM时也是如此。
另一种删除它们的安全方法是使用此向后迭代,因为从活动集合的末尾删除了项目,这不会导致任何项目在您尚未处理的集合中移动:
for (let i = node.childNodes.length - 1; i >= 0; i--) {
node.removeChild(node.childNodes[i]);
}
但是,我通常发现这比使用Array.from()
转换为静态数组更麻烦,因为您已经发现了。
答案 1 :(得分:1)
node.childNodes
是一个直播集合,所以当您从forEach中移除node
中的某个孩子时,您会使用迭代器。
https://developer.mozilla.org/en-US/docs/Web/API/Node/childNodes