为什么这个循环无限? JavaScript appendChild

时间:2012-11-12 18:01:51

标签: javascript while-loop infinite-loop appendchild

我发誓几天前工作正常......

elm = document.querySelectorAll(selector);
var frag = document.createDocumentFragment();
while (elm[0]){
    frag.appendChild(elm[0]);
}

是的,所以,这应该从我们的elm节点列表中追加每个节点。当第一个被追加时,第二个“移动”到节点列表中的第一个位置,因此下一个始终是elm[0]。当elm nodeList被完全追加时,应该停止。然而,这给了我一个无限循环。想法?

编辑 - 因为我已多次得到相同的答案...... nodeList不是数组,它是实时引用。当一个节点被“移动”(这里,附加)时,它应该从节点列表中自动删除。答案都说“你一遍又一遍地追加相同的元素” - 这就是发生的事情,不应该。 for循环不应该工作,因为当附加第一个节点时,下一个节点将获取其索引。

第二次编辑

所以现在的问题是“为什么nodeList表现为数组?”。每次在某处添加节点时,节点列表应该都要更新。最奇特的。

解决方案(如果有人需要处理实时+非实时节点列表的内容)

elm = (/*however you're getting a node list*/);
var frag = document.createDocumentFragment();
var elength = elm.length;
for (var b = 0; b<elength; b++){
    if (elm.length === elength){
        frag.appendChild(elm[b]);
    } else {
        frag.appendChild(elm[0].cloneNode());
    }
}

基本上,只需检查节点列表是否已更改长度。

6 个答案:

答案 0 :(得分:4)

来自MDN Docs

Element.querySelectorAll

摘要

返回从调用它的元素的所有元素的 非实时NodeList ,该元素与指定的CSS选择器组匹配。

语法

elementList = baseElement.querySelectorAll(selectors);

,其中

  • elementList是元素对象的非实时列表。
  • baseElement是一个元素对象。
  • selectors是一组要匹配的选择器。

从上面的文档中,您可以看到,当您将其附加到另一个元素时,它不会自动将其删除,因为它不是实时的。运行演示以显示该功能。

var selector = "div";
elm = document.querySelectorAll(selector);
var frag = document.createDocumentFragment();
console.log("before",elm.length);
frag.appendChild(elm[0]);
console.log("after",elm.length);

当上面的代码运行时,你会在控制台中获得。

before    3
after     3

如果你想做while循环,转换为数组并关闭()项目

var selector = "div";
var elmNodeLIst = document.querySelectorAll(selector);
var frag = document.createDocumentFragment();
var elems = Array.prototype.slice.call(elmNodeLIst );
while (elems.length) {
    frag.appendChild(elems.shift());
}
console.log(frag);

答案 1 :(得分:1)

您要在节点列表中追加第一项,一遍又一遍。您永远不会从数组中删除任何项目,但始终将第一个项目添加到片段中。而第一个总是一样的。

elm = document.querySelectorAll(selector);
var frag = document.createDocumentFragment();
while (elm.length){
    frag.appendChild(elm.shift());
}

这可能更接近你的意图。我们可以使用while (elm.length),因为当项目从数组中删除时,最终长度将为零,这是一个眩光值,循环将停止。

我们使用elm.shift()从数组中获取项目,因为该方法将返回索引为零的项目并将其从数组中删除,从而为我们提供了所需原始数组的变异。


我认为你认为这可能有效,因为一个节点只能有一个父节点。添加某处的含义将其从上一个父项中删除。但是,elm 不是 DOM片段。它只是一个包含元素引用的aray(或者可能是NodeList)。该数组不是这些元素的父节点,它只保存引用。

如果您有这样的循环可能会有效,因为您每次都要查询父节点的子节点,这是一个在您移动时实际会发生变化的节点列表:

elm = document.getElementById(id);
var frag = document.createDocumentFragment();
while (elm.children[0]){
    frag.appendChild(elm.children[0]);
}

答案 2 :(得分:0)

我不希望它首先发挥作用。

您的elm数组已初始化,且从未更新过。即使运行document.querySelectorAll(selector);的结果会返回不同的结果,也不会更改数组中的当前引用。

您需要重新运行选择器,或者在追加它后手动删除数组中的第一个元素。

答案 3 :(得分:0)

它是一个无限循环,因为它现在被写入,因为elm[0]总是引用相同的元素,并且该元素永远不为null(任何非null /非零结果都为真)。您也不对元素本身执行任何操作以使其在列表中进行迭代。您应该使用for循环而不是一段时间或者至少使用某种索引器来尝试遍历集合。

elm = document.querySelectorAll(selector);
var frag = document.createDocumentFragment();
for (i= 0; i < elm.length; i++)
{
    frag.appendChild(elm[i]);
}

修改

来自documentation

  

“现场”收藏

     

在大多数情况下,NodeList是一个实时集合。这意味着DOM树&gt;的更改将反映在集合中。

     

var links = document.getElementsByTagName('a'); // links.length === 2例如

  document.body.appendChild(links [0] .cloneNode(true)); //将另一个链接添加到文档中   //'链接'NodeList自动更新
  // links.length === 3现在。如果NodeList是document.querySelectorAll的返回值,则它是 NOT live。

根据此文档,您当前使用的方法表明您没有实时NodeList。因此,追加将永远不会修改原始列表。您需要在循环中修改您的用法以反映.cloneNode(true)的使用情况或手动迭代。

答案 4 :(得分:0)

elm [0]在上面的代码中是静态且不变的 修复如下

elm = document.querySelectorAll(".container");
var frag = document.createDocumentFragment();
console.log(elm);
var i=0;
while (elm[i]){
    frag.appendChild(elm[i++]);
}

答案 5 :(得分:0)

我实际上并没有太多关注代码(如果它有意义 - 从评论中判断 - 或者不是);但如果这几天前有效,那么问题出在你给代码选择器的输入中。

单位测试就派上用场了。如果您能记住代码工作的输入,那么您可以再次使用它并从那里开始调试。

否则,你只是骗自己。