我遇到了一个有趣的错误,其中 elt
未定义某些对 switchToReady
的调用。似乎 setTimeout
中的函数两次传递相同的 DOM 节点。
function switchToReady(elt) {
elt.setAttribute('transform', 'translate(17, 0)');
elt.classList.remove('compiling');
}
const compilingElts = document.getElementsByClassName('compiling');
for (let i = 0; i < compilingElts.length; i++) {
const randTime = Math.round(Math.random() * (2000 - 500)) + 500;
setTimeout(() => {
switchToReady(compilingElts[i]);
}, randTime);
}
最佳答案
getElementsByClassName
返回一个事件集合,这意味着如果集合中元素的类在您对其进行迭代时发生变化 ,或者如果您将具有该类的另一个元素添加到 DOM,那么您所在的索引(例如,如果 i
是 2
)可能不再引用旧的那里的元素 - 它可能引用集合中的下一项,或前一项,甚至可能是 undefined
。这种行为非常不直观,所以我建议改用 querySelectorAll
,它返回一个 static NodeList
,它不会在您对其进行迭代。
const compilingElts = document.querySelectorAll('.compiling');
querySelectorAll
的其他好处:
它接受作为参数的选择器字符串可以非常灵活 - 它不仅限于类
在较新的浏览器中,您可以直接在
NodeList
上调用forEach
,从而无需手动迭代并跟踪索引:compilingElts.forEach((elm) => { const randTime = Math.round(Math.random() * (2000 - 500)) + 500; setTimeout(() => { switchToReady(elm); }, randTime); });
在许多情况下,数组方法比 for
循环更好用。使用从 getElementsByClassName
生成的 HTMLCollection
在旧版浏览器上实现类似功能的一种方法是使用 Array.prototype.forEach
:
Array.prototype.forEach.call(
compilingElts,
(elm) => {
// do stuff with elm
}
);
(一个快捷方式是使用 [].forEach.call
代替,这将用更少的代码完成同样的事情,但是引用 Array.prototype
会更清楚一些海事组织)
关于javascript - JS DOM 操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53293794/