我正在制作一个脚本,从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失败的原因吗?
发布于 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>
HTMLCollection
s不直观的实时特性使得它们很难使用,除非你已经在期待它。在几乎所有情况下,使用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();
发布于 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);
});
https://stackoverflow.com/questions/51913329
复制相似问题