如何正确地遍历 getElementsByClassName

9 浏览
0 Comments

如何正确地遍历 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循环结构也没有帮助。

变量slides在每次迭代中表现得非常不确定,它会不断改变其长度和元素的顺序。

在我的情况下,正确的方法是如何遍历NodeList呢?我考虑过填充一些临时数组,但不确定如何做...

编辑:

我忘记提到的一个重要事实是可能会有一个幻灯片在另一个幻灯片中,这实际上是导致slides变量改变的原因,我刚刚在用户Alohci的帮助下发现了这一点。

对我来说,解决办法是先将每个元素克隆到一个数组中,然后逐个将数组传递给Distribute()

0
0 Comments

2021年的最新答案

.getElementsBy*方法返回一个活动的HTMLCollection,而不是一个NodeList,getElementsByName是一个例外。

这两个列表之间存在显着的差异。虽然HTMLCollection有两个方法,但NodeList有五个方法,包括NodeList.forEach,可以用来迭代NodeList。

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

标准规定:

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

永远不要迭代活动的HTMLCollection!

相反,将集合转换为数组,并对该数组进行迭代。或者使用.querySelectorALl获取元素,它可以给你一个静态的NodeList和更灵活的选择元素的方法。

如果你真的需要一个活动的元素列表,请使用最近的公共祖先元素作为上下文,而不是document。

值得注意的是,活动的NodeList也存在。活动的NodeList的例子包括Node.childNodes和getElementsByName的返回值。

所以你的意思是不要在getElementsBy*上使用for循环?

是的,完全正确,或者任何循环。

我不认为你对2013年左右的getElementsBy*返回NodeList的评论是正确的,你有证据吗?据我所知,它们一直是HTMLCollection。例如,即使是IE9也从getElementsByTagName返回HTMLCollection。类似地,我有一个Firefox v17的版本...

.J.Crowder在2013年左右的某个时期(我记得是2013年),一些浏览器在HTMLCollection和NodeList之间来回切换(不确定标准是否发生了变化)。我记得你是MDN的贡献者,你可以查看那个时候的旧文档。

作为上述声明的一个总体陈述,它绝对是不正确的。我刚刚尝试了Firefox v12(相对于Chrome来说,Firefox更容易获得旧版本),同样是HTMLCollection。(如果这是一种容易切换的东西,标准就不会有两个了。 :-))我会删除这个声明,除非你能提供一个引用(实际上,这样的历史信息没有多大意义)。

.J.Crowder好吧,如果你认为这样会改进这个帖子,我会删除它。不过,在问题中,也有“getElementsByClassName实际上不返回数组,而是返回一个NodeList”的说法,似乎被OP检查过了,因为甚至还有一个指向NodeList的链接。我会尽量找一些证据来支持我的说法,如果我找到了,我会在这里留言。

- 那会很有趣。关于OP——他们可能在使用为getElementsByClassName编写的polyfill(因为不是所有浏览器都支持它),该polyfill在底层使用querySelector。(无论他们是否意识到或者考虑提到它。)顺便问一下,如果你知道(或者找到了)获取Chrome旧版本的方法,请告诉我一声。

.J.Crowder问题中描述的行为是指一个活动列表(“在每次迭代中,它的长度和元素的顺序都会发生不确定的变化”),这个列表可能是HTMLCollection或者活动的NodeList,我们不知道。我不知道在哪里找到Chrome的旧版本,我也把它放在我的“列表”上。

- 啊,是的,完全正确!所以它不会是使用querySelectorAll的polyfill(无论如何,它也不会是querySelector,这是我写错了!)。

.J.Crowder我找到了一个“证据”,不是文档,但在这个答案中有一张2013年的Chrome DevTools的图片。列出的对象看起来像是HTMLCollection,但在Chrome中被命名为“NodeList”。如果要将我的原始文本重新添加到答案中,需要一个指向那个时候某些文档的链接。谢谢你指出这一点。

.J.Crowder在旧的whatwg标准中提到了getElementsByClassName集合的类型为“NodeList”。我不确定那个文档是否太旧了。

- 很有趣,谢谢!不过,那时至少有两个主要的浏览器没有这样做,但也许Chrome是这样做的(我很想找出来),并且是WHAT-WG中的(压倒性的)Google成员写的那一部分。 🙂

0
0 Comments

如何正确迭代通过getElementsByClassName获取的元素?这个问题的出现原因是因为nodeLists在每个浏览器中都没有forEach函数。解决方法有以下几种:

1. 使用querySelectorAll时,可以直接调用forEach函数:

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

2. 使用babel时,可以添加Array.from来将非nodeLists转换为forEach数组。但是Array.from在IE 11及以下版本的浏览器中不起作用:

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

3. 在最近的meetup中,我发现了另一种处理nodeLists没有forEach函数的方法:

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

需要注意的是,nodeLists在每个浏览器中都没有forEach函数。如果愿意修改原型,可以实现forEach函数:

if (!NodeList.prototype.forEach) {
    NodeList.prototype.forEach = Array.prototype.forEach;
}

另外,还有一个简单的解决方法是避免使用nodeLists的forEach函数,因为它在所有浏览器中可能不起作用。可以使用以下方法:

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

需要注意的是,如果想要使用style属性,应该使用驼峰命名法,例如`button.style.backgroundColor`,而不是`button.style.background-color`。因为连字符属性不被允许。

0
0 Comments

如何正确地遍历getElementsByClassName

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

nodeItem = nodeList.item(index)

因此:

var slides = document.getElementsByClassName("slide");

for (var i = 0; i < slides.length; i++) {

Distribute(slides.item(i));

}

我自己还没有尝试过(普通的for循环对我来说一直都有效),但你可以试试。

这是正确的解决方法,除非你尝试查找和更改具有相同类且相互包含的元素。我在问题的编辑中解释了我的解决方法。

当然,我没有考虑到这一点。

为什么会这样,我可以问一下吗?为什么没有实现这样一个功能,让我们能够像这样遍历节点for(var el in document.getElementsByClassName("foo")){}?

for ... of 现在允许你像这样遍历NodeList,例如 for (slide of slides) Distribute(slide)。浏览器支持不一致,但如果你使用转译,for ... of 将会被转换,而 NodeList.forEach 则不会。

0