1

我发誓这几天前工作得很好......

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

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

编辑- 因为我多次得到相同的答案...... AnodeList不是一个数组,它是一个实时参考。当一个节点被“移动”(此处为附加)时,它应该自动从节点列表中删除。答案都说“你一遍又一遍地附加相同的元素” - 这就是正在发生的事情,它不应该是。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());
    }
}

基本上,只是检查节点列表是否改变了长度。

4

6 回答 6

4

来自MDN 文档

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 循环,请转换为数组并 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);
于 2012-11-12T18:25:12.950 回答
1

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

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

这可能更接近您的意图。我们可以使用while (elm.length),因为随着项目从数组中删除,最终长度将为零,这是一个不稳定的值并且循环将停止。

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


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

如果你有这样的循环,你的循环可能会起作用,因为你每次都在查询父节点的子节点,一个节点列表实际上会随着你的移动而改变:

elm = document.getElementById(id);
var frag = document.createDocumentFragment();
while (elm.children[0]){
    frag.appendChild(elm.children[0]);
}
于 2012-11-12T18:06:48.623 回答
0

这是一个无限循环,因为它elm[0]总是引用同一个元素,并且该元素永远不会为空(任何非空/非零结果都是真的)。您也不会对元素本身做任何事情以使其遍历列表。您应该使用 for 循环而不是 while 或至少使用某种索引器来尝试遍历集合。

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

编辑:

文档中:

一个“活”的集合

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

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

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

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

于 2012-11-12T18:05:56.853 回答
0

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

elm = document.querySelectorAll(".container");
var frag = document.createDocumentFragment();
console.log(elm);
var i=0;
while (elm[i]){
    frag.appendChild(elm[i++]);
}
于 2012-11-12T18:07:40.243 回答
0

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

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

否则,你只是在欺骗自己。

于 2012-11-12T18:08:01.840 回答
0

我一开始没想到它会起作用。

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

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

于 2012-11-12T18:05:09.317 回答