2017-02-18 41 views
1

当我运行这个脚本时,页面不断加载并最终冻结。这是因为每次我创建一个元素时,主要DOMContentLoaded侦听器正在被调用?document.createElement是否继续触发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); 
     } 

    } 
}); 

回答

1

这是因为items是引用 “活动列表”。这意味着如果DOM中的任何更新都与原始选择器匹配,它们将反映在您的列表中。

由于您附加了div,并且您的选择器选择了所有元素,它将被添加到列表中,并将任何后续成员推到索引上,从而继续进行迭代。

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

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

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

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


最后,您可以使用现代功能来清理这一点。 - 这意味着随着匹配的元素更改可/将改变列表

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

它返回一个"live" node list

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

     // Work with newDiv 
    } 
}); 
+0

哇真棒,完美的作品! 第2行如何工作?它是如何摆脱“活”的问题的? –

+0

@EricNelson:很高兴帮助。 Array.from()只是一个将任何类似数组的对象转换为实际数组的实用程序,它不具有“活”特性。 '[... collection]'语法类似。它只是数组字面值语法,但使用'...'spread语法将集合复制到正在创建的数组中。 – 2017-02-19 00:47:17

0

,因为你有这个你得到一个连续加载页面。由于您的选择器适用于所有内容,因此您可以创建新的元素,匹配元素的集合会发生变化(变大)。这又会导致节点列表的长度发生变化,从而使循环继续运行。

你不应该尝试所有元素匹配,或者你应该使用另一种方法获得一个不返回现场节点列表中的元素,如querySelectoAll()

相关问题