【问题标题】:removing childNodes using node.childNodes.forEach使用 node.childNodes.forEach 删除子节点
【发布时间】:2018-06-26 21:31:27
【问题描述】:

传统上,在 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 方法没有像我想象的那样删除所有节点?我误会了什么?

【问题讨论】:

  • 您看到结果中的模式了吗? jsfiddle.net/k3pjkp2a 想想每次迭代时节点列表会发生什么。
  • “内置”一词是指 ECMAScript §4.2 的特性,宿主提供的任何东西都是宿主方法。所以 NodeList 实例的 forEach 方法不是“内置”的,它是一个宿主方法。 ;-)

标签: javascript dom ecmascript-6 nodelist


【解决方案1】:

node.childNodes 是一个实时集合。当您从中删除项目时,集合本身会被修改(在您迭代时生效)。尝试按原样对其进行迭代,会导致元素从集合中移除,并在迭代时在类似数组的结构中向下移动,从而导致错过节点。

例如,当您在集合中的第二个元素上调用 removeChild() 时,该元素本身就会从集合中删除。这会导致将第三个元素移动到集合中第二个元素所在的位置。现在,您的循环移动到集合中的第三个元素。但是,这将跳过现在位于第二个位置的元素,导致您永远无法删除它。

这意味着遍历实际集合并删除内容的唯一安全方法是向后遍历,因为从末尾删除内容不会导致其他元素改变它们在集合中的位置。从前面移除项目(这就是您所做的)确实会导致项目在集合中移动。

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() 转换为静态数组更麻烦,正如您已经发现的那样。

【讨论】:

  • 我明白了,感谢您花时间解释。
  • @m.chiang - DOM 的设计师究竟拥有什么,甚至为我们提供现场收藏,这对我来说是个谜。它们造成的麻烦比我想象的任何实用程序都要多。幸运的是,querySelectorAll() 几乎总是可以被使用,它会返回一个非实时结构。
  • @jfriend00——当然只是意见,但是获取一次集合并保留引用可能会更有效,因为知道它将根据其他代码对其所做的任何事情进行更新。否则,您每次都必须获取一个新副本,因为它可能自上次以来已被修改(并且可能的浏览器无论如何都会出于自己的目的保留实时集合)。但似乎很少有人编写代码来利用此功能(可能是因为大多数人直到为时已晚才知道它的存在)。
  • @RobG - 是的,但我从来没有发现需要甚至真正有用的编程情况,可能是因为只要你想要最新状态就查询 DOM 是如此之快。当你使用它时,当它无意中改变时,整个现场收集的东西会引起很多很多人的问题。我从来没有听过有人滔滔不绝地说 DOM API 是多么经过深思熟虑。相反,我们大多都使用某种包装器将其规范化为更易于使用的东西。
  • @jfriend00 — 在没有人能想到我们今天将如何使用 DOM API 的时候,API 被编写为语言中立。库和框架是为所有语言、API 等编写的。我不认为它们表示语言/API 的失败,它们只是帮助特定设计/实现的工具。至少 DOM API 似乎实现了各种库的更好特性,因此作者不必使用水晶球,只需通过使用来实现已知有用的东西,看看哪些有效,哪些无效。 ;-)
【解决方案2】:

node.childNodes 是一个实时集合,所以当你在 forEach 中从node 中移除一个子元素时,你会弄乱迭代器。

https://developer.mozilla.org/en-US/docs/Web/API/Node/childNodes

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-05-30
    • 1970-01-01
    • 1970-01-01
    • 2011-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-09
    相关资源
    最近更新 更多