从DOM中删除HTMLCollection元素

Mar*_*ine 8 javascript dom

我有一个段落元素的集合.有些是空的,有些只包含空格,有些则有内容:

<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>
<p></p>
<p> </p>
<p>    </p>
<p>&nbsp;</p>
<p> &nbsp;</p>
Run Code Online (Sandbox Code Playgroud)

我正在getElementsByTagName选择它们:

var paragraphs = document.getElementsByTagName('p');
Run Code Online (Sandbox Code Playgroud)

这将返回文档中的所有段落.我想删除所有这些,所以我想跑

for (var i = 0, len = paragraphs.length; i < len; i++) {
   paragraphs[i].remove();
}
Run Code Online (Sandbox Code Playgroud)

但我得到Uncaught TypeError: Cannot read property 'remove' of undefined错误.我觉得这很奇怪,但我会尝试添加一名后卫,看看会发生什么:

for (var i = 0, len = paragraphs.length; i < len; i++) {
   paragraphs[i] && paragraphs[i].remove();
}
Run Code Online (Sandbox Code Playgroud)

没有错误,但并未删除所有元素.所以我再次运行它,它删除之前未删除的一些元素.我再次运行它,最后从文档中删除所有段落.

我想知道我在这里缺少什么明显的细节.

演示问题

Fel*_*ing 25

问题是这paragraphs是一个实时列表.通过删除p元素,您也可以更改该列表.一个简单的解决方法是以相反的顺序迭代列表:

for (var i = paragraphs.length - 1; i >= 0; --i) {
  paragraphs[i].remove();
}
Run Code Online (Sandbox Code Playgroud)

替代解决方案是创建静态列表(非实时列表).你可以这样做:

然后,您可以按常规顺序遍历列表(使用for循环):

for (var i = 0; i < paragraphs.length; ++i) {
  paragraphs[i].remove();
}
Run Code Online (Sandbox Code Playgroud)

或(使用for...of循环):

for (var paragraph of paragraphs) {
  paragraph.remove();
}
Run Code Online (Sandbox Code Playgroud)

请注意,这.remove是一个相对较新的DOM方法,并不是每个浏览器都支持.有关详细信息,请参阅MDN文档.


为了说明这个问题,我们假设我们有一个包含三个元素的节点列表paragraphs = [p0, p1, p2].然后,当您遍历列表时会发生这种情况:

i = 0, length = 3, paragraphs[0] == p0  => paragraphs = [p1, p2]
i = 1, length = 2, paragraphs[1] == p2  => paragraphs = [p1]
i = 2, length = 1, END
Run Code Online (Sandbox Code Playgroud)

因此,在此示例中,p1不会删除它,因为它被跳过.

  • 你的两个 for 循环表达式非常简洁,但我必须建议它混淆了这个例子,因为它在第一次迭代时递减,并且条件依赖于递减值为 0 的虚假性。在那一点上,它会删除最后一个元素, 在索引 0? 对于那些需要它的人来说,一个传统的倒数语句是:```for (var i =paragraphs.length - 1; i &gt;= 0; i--)``` (2认同)

alj*_*gom 8

删除项目时,HTMLCollection的长度会发生变化.一种方法是使用while循环

while(paragraphs.length > 0) {
   paragraphs[0].remove();
}
Run Code Online (Sandbox Code Playgroud)

  • 仅当您保证删除**所有**子节点时,这才有效 (2认同)

vsy*_*ync 5

为什么这对你不起作用:

当您删除节点时,HTMLCollection正在发生变化(更改),长度与HTMLCollection数组的“实际”长度不同步

假设您有一个包含 2 个 DOM 节点的数组,并且正在迭代它。它应该迭代 2 次。下面的演示完美地说明了这一点并且很容易理解:

第一次迭代- 删除第一个节点,然后i递增。
第二次迭代- 现在i等于,1paragraphs.length现在也1因为此时只剩下一个段落。

这会导致一种不可能的情况,即1要求长度为 的数组访问位置 处的项目1,而唯一可用的位置是0(因为数组从位置开始0...)

访问数组(或类似数组的对象 HTMLCollection)中不存在的位置是非法的。

var paragraphs = document.getElementsByTagName('p')

for (var i = 0; i <= paragraphs.length; i++) {
   console.log(i, paragraphs.length)
   paragraphs[i].remove()
}
Run Code Online (Sandbox Code Playgroud)
<p>1</p>
<p>2</p>
Run Code Online (Sandbox Code Playgroud)

可能的修复:延迟删除节点

在下面的演示中,节点的删除是在所有迭代周期完成 进行的(延迟代码执行),这里的关键是利用第三个参数并传递将被缓存的节点作为超时回调的参数:setTimeout

var paragraphs = document.getElementsByTagName('p')

for (var i = 0, len = paragraphs.length; i < len; i++) {
   setTimeout(node => node.remove(),0 , paragraphs[i])
}
Run Code Online (Sandbox Code Playgroud)
<p>Pellentesque habitant....</p>
<p></p>
<p> </p>
<p>    </p>
<p>&nbsp;</p>
<p> &nbsp;</p>
Run Code Online (Sandbox Code Playgroud)

可能的修复:在每次迭代时检查节点是否存在

另外,重要的是不要增加i,因为数组的长度不断缩小,第一个项目在每次迭代时都会被删除,直到没有更多的项目为止

var paragraphs = document.getElementsByTagName('p')

for (var i = 0, len = paragraphs.length; i < len; ) {
   if(  paragraphs[i] )
     paragraphs[i].remove()
}
Run Code Online (Sandbox Code Playgroud)
<p>1</p>
<p>2</p>
<p>3</p>
Run Code Online (Sandbox Code Playgroud)

可能的修复:我几乎总是更喜欢反向迭代器

var paragraphs = document.getElementsByTagName('p')

for (var i = paragraphs.length; i--; ){
    paragraphs[i].remove() // could also use `paragraphs[0]`. "i" index isn't necessary
}
Run Code Online (Sandbox Code Playgroud)
<p>1</p>
<p>2</p>
<p>3</p>
Run Code Online (Sandbox Code Playgroud)