我正在编写一个脚本来删除我所有的 YouTube 评论。这段代码的每一行都可以删除一个注释,但是当我将其放入循环中时,我收到一个 Uncaught TypeError: Cannot read property 'click' of undefined
,当我运行时,它不会出现每行单独。我在想,如果我能找到一种在代码行之间休眠的方法,我就可以消除错误。
var myList = document.getElementsByClassName("dropdown-trigger style-scope ytd-menu-renderer");
for(i = 0; i < myList.length; i++) {
myList[i].click();
document.getElementsByClassName("style-scope ytd-menu-navigation-item-renderer")[4].click(); //error here
document.getElementById("confirm-button").click();
}
我尝试使用 setTimeout,如下所示:
var myList = document.getElementsByClassName("dropdown-trigger style-scope ytd-menu-renderer");
for(i=0; i<myList.length; i++) {
myList[i].click();
setTimeout(function(){document.getElementsByClassName("style-scope ytd-menu-navigation-item-renderer")[4].click();}, 1000);
setTimeout(function(){document.getElementById("confirm-button").click();}, 5000);
}
它返回了数字279,但没有错误,也没有删除评论。发生了什么?
最佳答案
以下所有选项都有点“hackish” - 但没有一致且简单的方法可以在点击事件完全处理后获得通知。 您可以将自己的单击事件监听器添加到目标元素,但是随后可以通过 stopPropagation 将其“静音”... 即使这样,也没有(简单)的方法可以知道 DOM 何时完成“重绘”作为点击事件的结果 - 你可以看看使用 MutationObserver ,我猜
我假设(永远不要这样做) document.getElementsByClassName("style-scope ytd-menu-navigation-item-renderer")[4]
所针对的元素通过单击 myList[i].click();
动态添加
这就是你遇到这个问题的原因。之后myList[i].click();
DOM 在“一段时间内”不会被“更新” - 所以,document.getElementsByClassName("style-scope ytd-menu-navigation-item-renderer")
很容易失败。
A potential issue is if deleting comments changes the number of elements targeted by
document.getElementsByClassName("dropdown-trigger style-scope ytd-menu-renderer");
- asHTMLCollection
s are "live", removing a DOM element will mutate theHTMLCollection
- only the last two code snippets will be immune to this possibility
因此,您可以通过四种不同的方式来实现此目的
<小时/>选项 1 - ES5,只是简单的回调
var DELAY = 0; // try 0, then try increasing values
var myList = document.getElementsByClassName("dropdown-trigger style-scope ytd-menu-renderer");
function confirmClick(callback3) {
document.getElementById("confirm-button").click();
setTimeout(callback3, DELAY);
}
function itemClick(callback2, callback3) {
document.getElementsByClassName("style-scope ytd-menu-navigation-item-renderer")[4].click();
setTimeout(callback2, DELAY, callback3);
}
function listClick(element, callback1, callback2, callback3) {
element.click();
setTimeout(callback1, DELAY, callback2, callback3);
}
function doOne(i) {
listClick(myList[i], itemClick, confirmClick, function() {
++i;
if (i < myList.length) {
doOne(i);
}
});
}
doOne(0);
<小时/>
As you are trying to "chain" multiple asynchronous (sort of) processes together,
Promise
's (seem) to make the code a little less cumbersome
选项 2 - Promise,但带有嵌套 setTimeout
的-
使用Array#reduce
将点击链接在一起,以便它们严格地一个接一个地处理
这太丑了,真的很丑,让金字塔建筑留给死去的埃及人, 包括来说明我想要实现的目标
var DELAY = 0; // try 0, then try increasing values
var myList = document.getElementsByClassName("dropdown-trigger style-scope ytd-menu-renderer");
var p = Promise.resolve();
for(i = 0; i < myList.length; i++) {
myList[i].click();
p = p.then(() => new Promise(resolve) => {
setTimeout(() => {
document.getElementsByClassName("style-scope ytd-menu-navigation-item-renderer")[4].click();
setTimeout(() => {
document.getElementById("confirm-button").click();
setTimeout(resolve, DELAY);
}, DELAY);
}, DELAY);
});
}
<小时/>
选项 3 - Promise 带有辅助函数 - 基本上是上面的代码,但添加了辅助函数来防止金字塔
const DELAY = 0; // try 0, then try increasing values
const clickThenDelay = element => new Promise(resolve => {
element.click();
setTimeout(resolve, DELAY);
});
var myList = document.getElementsByClassName("dropdown-trigger style-scope ytd-menu-renderer");
Array.from(myList).reduce((p, item) => {
return p
.then(() => clickThenDelay(item))
.then(() => clickThenDelay(document.getElementsByClassName("style-scope ytd-menu-navigation-item-renderer")[4]))
.then(() => clickThenDelay(document.getElementById("confirm-button")))
}, Promise.resolve());
<小时/>
选项 4 - 与选项 3 类似,但此选项在触发单击之前添加了一个单击事件处理程序,该处理程序在“处理”单击后被删除。
大概最后添加的处理程序是最后调用的。但不确定这是否得到保证。
此外,如果较早的处理程序调用event.stop[Immediate]Propagation()
,这将不起作用。
const DELAY = 0; // try 0, then try increasing values
const clickThenDelay = element => new Promise(resolve => {
const handleClick = () => {
element.removeEventListener('click', handleClick);
setTimeout(resolve, DELAY); // wait for repaint? Perhaps a MutationObserver event could be useful here?
}
element.addEventListener('click', handleClick);
element.click();
});
var myList = document.getElementsByClassName("dropdown-trigger style-scope ytd-menu-renderer");
Array.from(myList).reduce((p, item) => {
return p
.then(clickThenDelay(item))
.then(() => clickThenDelay(document.getElementsByClassName("style-scope ytd-menu-navigation-item-renderer")[4]))
.then(() => clickThenDelay(document.getElementById("confirm-button")))
}, Promise.resolve());
关于javascript - 如何减慢 JavaScript 代码的执行速度?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49164262/