将元素数组添加到DOM

7
我在HTML中创建了5个
,并希望将它们全部添加到我在JavaScript中创建的
包装器中。我尝试通过循环遍历这5个
,然后将
作为的子元素附加。

由于某种原因,循环会更改5个
的顺序,并且不会将它们全部附加在中。如何使用JavaScript将所有
添加到中,保持(HTML)顺序?
(请不要发布JQuery答案,因为这不是问题。我只想要JavaScript答案。)

JSFiddle

var wrapper = document.createElement('div'),
  myClass = document.getElementsByClassName('myClass');

myClass[0].parentElement.appendChild(wrapper);
wrapper.id = 'wrapper';

for (var key in myClass) {
  if (!myClass.hasOwnProperty(key)) continue;

  wrapper.appendChild(myClass[key]);
}
#wrapper {
  border: 2px solid green;
  color: brown;
}
<div class="myClass">First</div>
<div class="myClass">Second</div>
<div class="myClass">Third</div>
<div class="myClass">Fourth</div>
<div class="myClass">Fifth</div>


2
如果您想要有序,请不要使用for...in 进行迭代!!! 此外,该集合是实时的问题也存在。 - Oriol
1
此外,HTMLCollection除了成员的索引之外,还有其他可枚举属性(例如itemnamedItemlength以及具有这些属性的元素的ID)。 - RobG
@RobG 幸运的是,这些都是继承属性,因此 hasOwnProperty 检查避免了这个额外的问题。 - Oriol
@Oriol - length和元素ID不会被继承,但在不同的浏览器中可枚举性是不同的。 - RobG
5个回答

6
document.getElementsByClassName 方法返回一个类似于数组的 HTMLCollection 对象,它具有应使用的数字键。

例如:for (var i = 0; i < myClass.length; ++i)

一旦您使用增量数字索引,您会注意到它实际上与您的 key in myClass 表现相同,这是相当合理的,因为 key 就是数字索引。

发生的情况是,HTMLCollection 表示文档顺序中的元素(称为“动态列表”),它反映了 DOM 中的更改,并且您通过将其附加到包装器元素来移动它们(因此在 HTMLCollection 内的顺序也会更改)。

有几个技巧可以解决这个问题,最接近您当前设置的方法是从尾到头遍历 HTMLCollection,并使用 insertBefore 而不是 appendChild

for (var len = myClass.length - 1; len >=0; --len) {
    wrapper.insertBefore(myClass[len], wrapper.firstChild);
}

insertBefore fiddle

这个示例之所以有效,是因为包装器(在您的示例中)位于您要移动到其中的元素之后,因此不会更改元素的顺序。

还有另一种(更简单的)方法:document.querySelectorAllquerySelectorAll 方法返回一个(静态的)NodeList,因此您可以安全地假定在移动节点时顺序不会改变。

语法比getElementsByClassname更方便,因为它使用CSS选择器(与我们不提及的流行javascript框架类似)

querySelectorAll fiddle


另一种方法是在将元素添加到DOM之前先将它们添加到wrapper中,这样当它们被移动时,它们就会从DOM中删除,因此集合也会被删除。 - RobG
@Rogier Spieker 哈哈!你最后一句话让我笑了!我不是那个意思,我只是不想在没有 JQuery 标签的情况下得到 JQuery 的答案。但我喜欢你的回答!谢谢!我想我会选择 document.querySelectorAll 方法。 - Jessica
@RogierSpieker 哦,我明白了。你建议在添加到DOM之前或之后将元素添加到包装器中? - Jessica
1
@Jessica 我会尽可能晚地将包装器附加到DOM上。我会尽量减少DOM操作的数量,因为这些操作会在浏览器中触发回流。对DOM的最小更改是将元素添加到包装器中,然后将包装器添加到文档中,所以这将是我在此场景中的选择。 - Rogier Spieker
使用 DocumentFragment - caub
显示剩余2条评论

3

一步一步来看你的for循环:

1. myClass[First, Second, Third, Fourth, Fifth]; wrapper[] ; key = 0
2. myClass[Second, Third, Fourth, Fifth] ; wrapper[First]; key = 1

现在你需要选择第三个而不是第二个,因为键是1,但你需要获取索引为0的项。这也给出了解决方法:总是取位置为0的项。

var wrapper = document.createElement('div'),
  myClass = document.getElementsByClassName('myClass');

myClass[0].parentElement.appendChild(wrapper);
wrapper.id = 'wrapper';

for (var i = 0; i < myClass.length; i++) {
  wrapper.appendChild(myClass[0]);
}
#wrapper {
  border: 2px solid green;
  color: brown;
}
<div class="myClass">First</div>
<div class="myClass">Second</div>
<div class="myClass">Third</div>
<div class="myClass">Fourth</div>
<div class="myClass">Fifth</div>


1
您正在动态更改集合(在循环本身中)通过从中删除项目,这就是为什么它表现出奇怪的行为。以下是应该实际工作的代码:
var wrapper = document.createElement('div'),
myClass = document.getElementsByClassName('myClass'),
myClassParent = myClass[0].parentNode;

while (myClass.length) {
    wrapper.appendChild(myClass[0]);
}
myClassParent.appendChild(wrapper);
wrapper.setAttribute('id','wrapper');

https://jsfiddle.net/byd9fer3/1/


你也可以使用 for 循环来构建它,这只是更容易检查的条件。 - Maciej Kwas
我尝试使用for循环,但结果并不如预期。这是一个更新后的JSFiddle链接。 - Jessica
@Jessica 阅读Gavriel的答案,然后阅读Oddadmix的答案和评论,你就会明白为什么在这种情况下从最低索引到最高索引迭代的for循环不起作用,但反向迭代则可以。 - Maciej Kwas
一切都在别人的答案中 https://jsfiddle.net/byd9fer3/3/ ,现在你所需要的只是一点逻辑感。 - Maciej Kwas

0

我已经为您准备好了一个带有代码的简单工作版本。谢谢。

<html>
<body>

<button onclick="myFunction()">Try it</button>

<p><strong>Note:</strong> The getElementsByClassName() method is not supported in Internet Explorer 8 and earlier versions.</p>

<div class="myClass">First</div>
<div class="myClass">Second</div>
<div class="myClass">Third</div>
<div class="myClass">Fourth</div>
<div class="myClass">Fifth</div>

<script>
function myFunction() {

    var wrapper = document.createElement('div');
    var x = document.getElementsByClassName("myClass");
    

for(var i=0; i < x.length;++i){
    var newdiv = document.createElement('div');
    newdiv.appendChild(document.createTextNode(x[i].innerHTML));
    wrapper.appendChild(newdiv);
   }

 document.body.appendChild(wrapper);
}
</script>

</body>
</html>


我认为将 innerHTML 设置为 textContent 不是一个好主意。考虑使用 cloneNode - Oriol

0
最简单的方法是使用Element的append方法,并像这样使用它: Element.prototype.append.apply(domElement, arrayOfElements);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接