javascript - JS DOM 操作

标签 javascript html asynchronous dom

我遇到了一个有趣的错误,其中 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,那么您所在的索引(例如,如果 i2)可能不再引用旧的那里的元素 - 它可能引用集合中的下一项,或前一项,甚至可能是 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/

相关文章:

Javascript 代码在 while 循环中不起作用

javascript - 从 jQuery Datatable 隐藏列中获取值

python - 在 Tornado 中运行异步后台任务

javascript - Ember JS : Async Model

javascript - 如何获取检查的输入长度

javascript - 如何对对象使用 Underscore.js 过滤器?

html - 将链接放在图片中

javascript - 从 Promise : [Object Object] 返回一个值

javascript - 使用 jquery 根据包含类选择父 DIV 内容

javascript - jQuery动画批量图像