如何正确迭代 getElementsByClassName

我是 Javascript 初学者。

我正在通过 window.onload初始化网页,我必须找到他们的类名称(slide)的一串元素,并根据一些逻辑将它们重新分配到不同的节点。我有一个函数 Distribute(element),它接受一个元素作为输入并进行分布。我想做一些类似的事情(例如 给你给你) :

var slides = getElementsByClassName("slide");
for(var i = 0; i < slides.length; i++)
{
Distribute(slides[i]);
}

然而这对我来说并没有魔力,因为 getElementsByClassName实际上并不返回数组,而是返回一个 NodeList,它是..。

这是我的推测。

... 在函数 Distribute中被更改(DOM 树在该函数中被更改,并发生某些节点的克隆)。For-each循环结构也不起作用。

变量幻灯片的行为真的是不确定的,通过每次迭代,它改变它的长度和元素的顺序疯狂。

在我的例子中,循环访问 NodeList 的正确方法是什么?我在考虑填充一些临时数组,但不知道如何做到这一点..。

编辑:

我忘记提到的一个重要事实是,可能有一张幻灯片在另一张幻灯片中,这实际上是什么改变了 slides变量,因为我刚刚发现感谢用户 你好

我的解决方案是首先将每个元素克隆到一个数组中,然后将数组一个一个地传递到 Distribute()中。

278738 次浏览

根据 MDN ,从 NodeList中检索项目的方法是:

nodeItem = nodeList.item(index)

因此:

var slides = document.getElementsByClassName("slide");
for (var i = 0; i < slides.length; i++) {
Distribute(slides.item(i));
}

我自己还没有尝试过这种方法(正常的 for循环对我来说总是很有效) ,但是试一试吧。

我按照 你好的建议反向循环,因为它是一个活的 nodeList。这是我为那些好奇的人所做的。

  var activeObjects = documents.getElementsByClassName('active'); // a live nodeList


//Use a reverse-loop because the array is an active NodeList
while(activeObjects.length > 0) {
var lastElem = activePaths[activePaths.length-1]; //select the last element


//Remove the 'active' class from the element.
//This will automatically update the nodeList's length too.
var className = lastElem.getAttribute('class').replace('active','');
lastElem.setAttribute('class', className);
}

你总是可以使用数组方法:

var slides = getElementsByClassName("slide");
Array.prototype.forEach.call(slides, function(slide, index) {
Distribute(slides.item(index));
});

如果使用新的 querySelectorAll,则可以直接调用 forEach。

document.querySelectorAll('.edit').forEach(function(button) {
// Now do something with my button
});

根据下面的注释,.nodeList 没有 forEvery 函数。

如果对 babel 使用这个函数,您可以添加 Array.from,它将把非节点列表转换为 forEvery 数组。Array.from不能在以下浏览器中工作,包括 IE11。

Array.from(document.querySelectorAll('.edit')).forEach(function(button) {
// Now do something with my button
});

在昨晚的会面中,我发现了另一种处理节点列表的方法

[...document.querySelectorAll('.edit')].forEach(function(button) {
// Now do something with my button
});

浏览器对[ ... ]的支持

显示为节点列表

Showing as Node List

显示为数组

Showing as Array

 <!--something like this-->
<html>
<body>






<!-- i've used for loop...this pointer takes current element to apply a
particular change on it ...other elements take change by else condition
-->




<div class="classname" onclick="myFunction(this);">first</div>
<div class="classname" onclick="myFunction(this);">second</div>




<script>
function myFunction(p) {
var x = document.getElementsByClassName("classname");
var i;
for (i = 0; i < x.length; i++) {
if(x[i] == p)
{
x[i].style.background="blue";
}
else{
x[i].style.background="red";
}
}
}




</script>
<!--this script will only work for a class with onclick event but if u want
to use all class of same name then u can use querySelectorAll() ...-->








var variable_name=document.querySelectorAll('.classname');
for(var i=0;i<variable_name.length;i++){
variable_name[i].(--your option--);
}






<!--if u like to divide it on some logic apply it inside this for loop
using your nodelist-->


</body>
</html>

我在迭代中遇到过类似的问题,所以我来到了这里。也许其他人也犯了和我一样的错误。

在我的例子中,选择器根本不是问题所在。问题是我搞错了 javascript 代码: 我有一个循环和一个子循环。子循环也使用 i作为计数器,而不是 j,因此由于子循环覆盖了主循环的 i值,所以这个子循环从未进行第二次迭代。

var dayContainers = document.getElementsByClassName('day-container');
for(var i = 0; i < dayContainers.length; i++) { //loop of length = 2
var thisDayDiv = dayContainers[i];
// do whatever


var inputs = thisDayDiv.getElementsByTagName('input');


for(var j = 0; j < inputs.length; j++) { //loop of length = 4
var thisInput = inputs[j];
// do whatever


};


};

2021年的最新答案

.getElementsBy*方法返回一个活的 HTMLCollection,而不是 节点列表getElementsByName是一个异常。

这两份名单之间有着显著的差异。HTMLCollection 有两个方法,而 NodeList 有五个方法,包括 NodeList.forEach,它们可以用来迭代 NodeList。

实时集合是有问题的,因为没有办法保持集合的内部更新。为了在当前的每个 HTMLCollection 实现中实现可靠的集合 每次访问集合时都会遍历 DOM。实际上,这意味着每次访问活动集合的成员(包括长度)时,浏览器都会遍历整个文档以查找特定的元素。

标准说:

如果集合是活动的,则该对象上的属性和方法必须对实际的基础数据进行操作,而不是对数据的快照进行操作。

永远不要迭代实时 HTMLCollection!

相反,将集合转换为数组,并迭代该数组。或者使用 .querySelectorAll获取元素,它为您提供了一个静态 NodeList 和一种更灵活的选择元素的方法。

如果您确实需要一个活动的元素列表,那么使用尽可能接近的公共祖先元素作为上下文,而不是使用 document

值得注意的是,还存在活动节点列表。活动节点列表的例子是 Node和返回值 GetElementsByName

你可以使用 Object.values + for...of循环:

const listA = document.getElementById('A');
const listB = document.getElementById('B');
const listC = document.getElementById('C');
const btn = document.getElementById('btn');


btn.addEventListener('click', e => {
// Loop & manipulate live nodeLList
for (const li of Object.values(listA.getElementsByClassName('li'))) {
if (li.classList.contains('active')) {
listB.append(li);
} else {
listC.append(li);
}
}
});
ul {
display: inline-flex;
flex-direction: column;
border: 1px solid;
}


ul::before {
content: attr(id);
}


.active {
color: red;
}


.active::after {
content: " (active)";
}
<ul id="A">
<li class="li active">1. Item</li>
<li class="li">2. Item</li>
<li class="li">3. Item</li>
<li class="li active">4. Item</li>
<li class="li active">5. Item</li>
<li class="li">6. Item</li>
<li class="li active">7. Item</li>
<li class="li">8. Item</li>
</ul>


<button id="btn">Distribute A</button>


<ul id="B"></ul>
<ul id="C"></ul>

一句话:
Object.values(listA.getElementsByClassName('li')).forEach(li => (li.classList.contains('active') ? listB : listC).append(li))

2022年最新情况

最快和最短的解决方案

[...document.getElementsByClassName('className')].forEach(el => {
// Do something with each element
})

为什么会有用?

迭代实时 HTML 集合效率极其低下

正如上面的 斯蒂克斯答案中所提到的,[...htmlCollection]将类集合转换为一个数组。 有必要将其转换为数组,因为直接迭代一个活动的 HTMLCollection 将是非常低效的。正如 Teemu在“ 永远不要迭代实时 HTMLCollection!”上面所写的:

实时集合是有问题的,因为没有办法保持集合的内部更新。为了实现可靠的集合,在 HTMLCollection 的每个当前实现中,每次访问集合时都要遍历 DOM。在实践中,这意味着,每次访问实时集合的成员时(包括长度),浏览器遍历整个文档,以找到特定的元素。

为什么它是最快和最有效的?

请注意,使用 [...arr] 是将 htmlCollection 转换为数组的最快和最有效的方法。 哈波所有方法的性能比较可以在这里找到: Http://jsben.ch/h2ifa

(参见有关 htmlCollection 转换为数组 给你的所有详细信息)