首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >JavaScript - removeChild()有时会失败,但并非总是失败

JavaScript - removeChild()有时会失败,但并非总是失败
EN

Stack Overflow用户
提问于 2018-08-19 07:41:01
回答 2查看 77关注 0票数 -3

我正在制作一个脚本,从twitch chat上的查看者列表中删除机器人的用户名。

我的代码:

const bots = ['0_applebadapple_0','bananennanen','commanderroot', 'decafsmurf', 'electricallongboard','electricalskateboard','lanfusion','skinnyseahorse','slocool', 'woppes'] //list to check against
const hide = () =>{
    const buttons = document.getElementsByClassName('chat-viewers-list__button') //gets HTMLCollection of elements
    Array.prototype.forEach.call(buttons, button => {
        if (bots.includes(button.dataset.username)) {
            button.parentNode.parentNode.removeChild(button.parentNode)
        }} //runs a for each through the HTMLCollection, remove parent element if username is in list
    )}
hide()

注意:我正在获取button元素并删除它们的父div。

在与5到7个机器人聊天时,它只删除3-4个匹配。我已经确认其他名称匹配成功(大小写不是问题,我删除了这段代码),并且一旦removeChild匹配(删除了这段代码),就无法执行任何操作。

你知道removeChild失败的原因吗?

EN

回答 2

Stack Overflow用户

发布于 2018-08-19 07:52:14

getElementsByClassName返回一个活动的HTMLCollection。它不是静态的;如果在迭代集合时文档中的chat-viewers-list__button类的集合发生了变化,则可能不是每个元素都会调用回调。例如,如果集合从长度6开始,如果行

name.parentNode.parentNode.removeChild(name.parentNode)

在第一次迭代中运行,并且删除了元素,则names现在的长度不再是6,而是5;以前位于索引1的元素现在位于索引0。然后,您的下一次迭代将检查names[1] -从而跳过最初位于names[1]的元素。

下面是一个例子。集合中最初有4个元素,但由于在迭代时删除了2个元素,回调函数只触发了两次:

const names = document.getElementsByClassName('chat-viewers-list__button') //gets HTMLCollection of elements
Array.prototype.forEach.call(names, name => {
  console.log('iteration');
  name.remove();
});
<div class="chat-viewers-list__button">a</div>
<div class="chat-viewers-list__button">b</div>
<div class="chat-viewers-list__button">c</div>
<div class="chat-viewers-list__button">d</div>

解决方案是使用querySelectorAll,它返回一个静态NodeList,当你试图迭代它时,它不会改变:

const names = document.querySelectorAll('.chat-viewers-list__button');
Array.prototype.forEach.call(names, name => {
  console.log('iteration');
  name.remove();
});
<div class="chat-viewers-list__button">a</div>
<div class="chat-viewers-list__button">b</div>
<div class="chat-viewers-list__button">c</div>
<div class="chat-viewers-list__button">d</div>

HTMLCollections不直观的实时特性使得它们很难使用,除非你已经在期待它。在几乎所有情况下,使用querySelectorAll可能更好;它是静态的,更灵活(即,它接受选择器字符串,可以是任何字符串),并且可以在现代浏览器中使用forEach直接迭代。

与使用.parentNode.removeChild(button.parentNode)相比,使用.remove()也不太繁琐

const bots = ['0_applebadapple_0', 'bananennanen', 'commanderroot', 'decafsmurf', 'electricallongboard', 'electricalskateboard', 'lanfusion', 'skinnyseahorse', 'slocool', 'woppes'] //list to check against;
const hide = () => {
  const buttons = document.querySelectorAll('.chat-viewers-list__button');
  Array.prototype.forEach.call(buttons, button => {
      if (bots.includes(button.dataset.username)) {
        button.parentNode.remove();
      }
    }
  )
}
hide();
票数 1
EN

Stack Overflow用户

发布于 2018-08-19 07:59:36

document.getElementsXXX()函数返回一个HtmlCollection,它是页面上元素的实时视图。Array.prototype.forEeach.call类似于循环遍历数组,因此当您删除一个元素时,下一个元素现在具有该索引,长度减少,但循环跳过该元素()移动到下一个索引:

-- removes 1st, 3rd, 5th, 7th, 9th elements
const divs = document.getElementsByTagName('div');
Array.prototype.forEach.call(divs, div => {
  console.log(`divs.length is now ${divs.length}`);
  div.parentNode.removeChild(div);
});

如果您首先使用Array.from创建了一个实数组并遍历该数组,那么它应该会按预期工作()。

-- removes as expected
const divs = Array.from(document.getElementsByTagName('div'));
divs.forEach(div => {
  console.log(`divs.length is now ${divs.length}`);
  div.parentNode.removeChild(div);
});
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51913329

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档