简体   繁体   English

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

[英]Why is this while loop infinite? JavaScript appendChild

I swear this was just working fine a few days ago... 我发誓这几天前工作得很好...

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

Right, so, this should append each node from our elm node list. 正确,因此,这应该附加到我们的elm节点列表中的每个节点。 When the first one is appended, the second "moves" to the first position in node list, hence the next one is always elm[0] . 当附加第一个时,第二个“移动”到节点列表中的第一个位置,因此下一个始终为elm[0] It should stop when the elm nodeList is completely appended. 完全附加elm nodeList后,它应该停止。 However, this is giving me an infinite loop. 但是,这给了我无限循环。 Thoughts? 有什么想法吗?

EDIT - because I've gotten the same answer several times... A nodeList is not an array, it is a live reference. 编辑 -因为我已经多次得到相同的答案... nodeList不是数组,而是实时引用。 When a node is "moved" (here, appended) it should be removed automatically from the node list. 当节点被“移动”(在此附加)时,应将其自动从节点列表中删除。 The answers all saying "you're appending the same element over and over" - this is what's happening, it shouldn't be. 答案都说“您要一遍又一遍地添加相同的元素”-这是正在发生的事情,不应该这样。 A for loop shouldn't work, because when the first node is appended, the next node takes its index. for循环不应该工作,因为当第一个节点附加,下一个节点需要它的索引。

2nd EDIT 第二次编辑

So the question is now "why is the nodeList behaving as an array?". 所以现在的问题是“为什么nodeList表现为数组?”。 The node list SHOULD be updating every time a node is being appended somewhere. 每当节点被附加到某处时,节点列表应该被更新。 Most peculiar. 最奇特的。

Solution (in case someone needs something to handle live + non-live node lists) 解决方案 (以防某人需要某些东西来处理实时和非实时节点列表)

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());
    }
}

Basically, just checking to see if the node list has changed length. 基本上,仅检查节点列表的长度是否已更改。

From the MDN Docs MDN文档

Element.querySelectorAll Element.querySelectorAll

Summary 摘要

Returns a non-live NodeList of all elements descended from the element on which it is invoked that match the specified group of CSS selectors. 返回一个非活动的NodeList ,其中包含与指定CSS选择器组匹配的,从其被调用的元素派生的所有元素。

Syntax 句法

elementList = baseElement.querySelectorAll(selectors);

where 哪里

  • elementList is a non-live list of element objects. elementList是元素对象的非实时列表。
  • baseElement is an element object. baseElement是一个元素对象。
  • selectors is a group of selectors to match on. selectors是一组要匹配的选择器。

From the docs above you can see it does not automatically remove it when you append it to another element since it is non live. 从上面的文档中可以看到,将其附加到另一个元素时,它不会自动删除,因为它是不活跃的。 Run a demo to show that feature. 运行演示以显示该功能。

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);

When the code above runs, in the console you get. 当上面的代码运行时,您将在控制台中看到。

before    3
after     3

If you want to do the while loop, convert to an array and shift() the items off 如果要执行while循环,请将其转换为数组并将shift()项关闭

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);

You are appending the first item in the node list, over and over and over. 您将一遍又一遍地追加节点列表中的第一项。 You never removing any items from the array, but always adding the first one to the fragment. 您永远不会从数组中删除任何项目,而总是将第一个添加到片段中。 And the first one is always the same. 和第一个总是一样的。

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

This may be closer to what you meant to do. 这可能更接近您的意图。 We can use while (elm.length) because as items get removed form the array, eventually length will be zero which is a flasy value and the loop will stop. 我们可以使用while (elm.length)因为随着从数组中删除项目,最终length将为零,这是一个脆弱的值,并且循环将停止。

And we use elm.shift() to fetch the item from the array because that method will return the item at index zero and remove it from the array, which gives us the mutation of the original array we need. 并且我们使用elm.shift()从数组中获取项目,因为该方法将返回索引为零的项目并将其从数组中删除,这使我们可以elm.shift()所需的原始数组。


I think you thought this might work because a node can only have one parent. 我认为您认为这可能有效,因为一个节点只能有一个父节点。 Meaning adding somewhere removes it from the previous parent. 意味着在某处添加将从先前的父项中删除。 However, elm is not a DOM fragment. 但是, elm 不是 DOM片段。 It's just a aray (or perhaps a NodeList) that holds references to element. 它只是保存对元素的引用的Aray(或者也许是NodeList)。 The array is not the parent node of these elements, it just holds references. 数组不是这些元素的父节点,它仅保存引用。

Your loop might work if you had it like this, since you are query the parent node each time for its children, a list of node that will actually change as you move around: 如果您有这样的话,您的循环可能会起作用,因为您每次都在父节点中查询其子节点,因此节点列表实际上会随着您的移动而改变:

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

I wouldn't have expected it to work in the first place. 我本来不会期望它首先起作用的。

Your elm array is initialized, and never updated. 您的elm数组已初始化,并且从未更新。 Even if the result from running document.querySelectorAll(selector); 即使来自运行document.querySelectorAll(selector); would return something different, this doesn't change your current references in the array. 会返回不同的值,这不会更改您当前在数组中的引用。

You would either need to rerun the selector, or manually remove the first element in the array after appending it. 您可能需要重新运行选择器,或者在附加选择器之后手动删除该数组中的第一个元素。

It's an infinite loop as it's written right now because elm[0] always refers to the same element, and that element is never null (any non-null/non-zero result would be true). 这是一个无限循环,因为elm[0]始终引用相同的元素,并且该元素永远不会为null(任何非null /非零结果都为true)。 You also don't do anything with the elements themselves to make it iterate across the list. 您也无需对元素本身进行任何操作以使其遍历整个列表。 You should be using a for loop instead of a while or at least having some kind of indexer to try to traverse the collection. 您应该使用for循环,而不要使用一会儿,或者至少要使用某种索引器来尝试遍历该集合。

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

Edit: 编辑:

From the documentation : 文档中

A "live" collection “现场”收藏

In most cases, the NodeList is a live collection. 在大多数情况下,NodeList是一个实时集合。 This means that changes on the DOM tree >are going to be reflected on the collection. 这意味着DOM树上的更改>将反映在集合上。

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

document.body.appendChild( links[0].cloneNode(true) ); document.body.appendChild(links [0] .cloneNode(true)); // another link is added to the document //将另一个链接添加到文档中
// the 'links' NodeList is automatically updated //'links'NodeList自动更新
// links.length === 3 now. //现在是links.length === 3。 If the NodeList is the return value of document.querySelectorAll, it is NOT live. 如果NodeList是document.querySelectorAll的返回值,则它不是活动的。

Going on this documentation, your current usage of the method indicates you do not have a live NodeList. 继续阅读本文档,您当前使用的方法表示您没有活动的NodeList。 Thus appending will never modify the original list. 因此,追加将永远不会修改原始列表。 You will either need to modify your usage within the loop to mirror this usage of .cloneNode(true) or iterate manually. 您将需要在循环中修改用法以反映.cloneNode(true)用法,或者手动进行迭代。

elm[0] is static and unchanging in above code fix is as below elm [0]是静态的,并且在上面的代码修复中不变如下

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

I didn't actually focus much on the code (and if it made sense -judging from the comments- or not); 我实际上并没有在代码上投入太多的精力(如果有意义,从注释中判断)。 but if this worked a few days ago then the problem is in the input you are giving to your code selector. 但是如果这几天前奏效,那么问题出在您提供给代码选择器的输入中。

That's when Unit Testing comes in handy. 那时单元测试就派上用场了。 If you can remember the input with which the code worked, then you can make it work again and start debugging from there. 如果您能记住代码所使用的输入,则可以使其再次起作用并从此处开始调试。

Otherwise, you are just lying to yourself. 否则,您就是在自欺欺人。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM