document.createElement是否继续触发DOMContentLoaded事件侦听器?

时间:2017-02-18 21:52:39

标签: javascript html dom

当我运行此脚本时,页面会不断加载并最终冻结。这是因为每次我创建一个元素时,都会调用主DOMContentLoaded侦听器吗?

如果是这样,我怎样才能停止这种递归行为,只需在每个预先存在的节点上添加一个节点?

//Waits for page to load
document.addEventListener('DOMContentLoaded', function() {

  //Get all elements
    var items = document.getElementsByTagName("*");

    //Loop through entire DOM
    for (var i = 0; i < items.length; i++) {

        //If it is not a text node
        if (!(items[i].nodeType == 3)){

          //Create a div
          var newDiv = document.createElement("div");

          //Add div to current object
          items[i].appendChild(newDiv);
        }

    }
});

2 个答案:

答案 0 :(得分:2)

这是因为items引用了“实时列表”。这意味着,如果DOM的任何更新都与原始选择器匹配,则会反映在列表中。

因为您正在附加div,并且您的选择器选择了所有元素,它会被添加到列表中,将任何后续成员推送到索引,因此迭代将继续。

为避免这种情况,请在迭代前制作该集合的非实时副本。

var items = Array.from(document.getElementsByTagName("*"));

而且仅供参考,if (!(items[i].nodeType == 3)){可以删除,因为getElementsByTagName永远不会返回文字节点。

如果您支持非常旧版本的IE,您可能需要检查.nodeType === 1,因为其中一些旧版本在使用"*"时包含注释节点。

最后,您可以使用现代功能来清理它。

document.addEventListener('DOMContentLoaded', () => {
    for (const el of [...document.getElementsByTagName("*")]) {
      var newDiv = el.appendChild(document.createElement("div"));

      // Work with newDiv
    }
});

答案 1 :(得分:0)

你得到一个连续加载页面,因为你有这个:

var items = document.getElementsByTagName("*");

返回 "live" node list - 表示匹配元素更改时可以/将会更改的列表。由于您的选择器适用于所有内容,然后您创建新元素,因此匹配元素集会更改(它会变大)。这反过来会导致节点列表的长度发生变化,从而使循环继续运行。

您不应该尝试匹配所有元素,或者您应该使用另一种方法来获取不返回实时节点列表的元素,例如querySelectoAll()