通过 ClassName 获取所有元素并更改 ClassName

IT技术 javascript
2021-03-05 11:53:26

我想...

  1. 扫描文档中所有具有特定类名的元素
  2. 对该元素的innerHTML 执行一些关键功能
  3. 更改该元素的类名,以便以后再扫描时不会重做该元素

我认为这段代码可以工作,但由于某种原因,它在第一个实例之后中断了循环,并且元素的类名永远不会改变。请不要框架。

function example()
{
    var elementArray;

    elementArray = document.getElementsByClassName("exampleClass");

    for(var i = 0; i < elementArray.length; i++)
    {
        // PERFORM STUFF ON THE ELEMENT
        elementArray[i].setAttribute("class", "exampleClassComplete");
        alert(elementArray[i].className);
    }   
}

编辑(最终答案) ——这是最终产品以及我如何在我的网站中实施@cHao 的解决方案。目标是获取页面上的各种时间戳并将它们更改为之前的时间。谢谢大家的帮助,我从这个问题中学到了很多。

function setAllTimeAgos()
{
    var timestampArray = document.getElementsByClassName("timeAgo");

    for(var i = (timestampArray.length - 1); i >= 0; i--)
    {
        timestampArray[i].innerHTML = getTimeAgo(timestampArray[i].innerHTML);
        timestampArray[i].className = "timeAgoComplete";
    }
}
5个回答

问题是返回给您的 NodeList 是“实时”的——它会随着您更改类名而发生变化。也就是说,当您更改第一个元素的类时,列表立即比原来短一个元素。

试试这个:

  while (elementArray.length) {
    elementArray[0].className = "exampleClassComplete";
  }

(不需要用来setAttribute()设置“class”值——只需更新“className”属性。setAttribute()在旧版本的 IE 中使用无论如何都行不通。)

或者,将 NodeList 转换为普通数组,然后使用索引迭代:

  elementArray = [].slice.call(elementArray, 0);
  for (var i = 0; i < elementArray.length; ++i)
    elementArray[i].className = "whatever";

正如评论中指出的那样,这具有不依赖NodeList 对象语义的优点(还要注意,再次感谢评论,如果您需要它在旧版本的 Internet Explorer 中工作,您必须编写一个显式循环来将元素引用从 NodeList 复制到数组。)

@gmustudent:那没什么不同。这只是相同方法的不同路径。
2021-04-28 11:53:26
所以第二次我更改了类名,元素是从列表中获取的。那是疯狂!达到时间限制时将接受。感谢您的帮助。
2021-04-30 11:53:26
我不会建议while (elementArray.length) {作为条件。它不像一个数组,它只读取缓存的属性。每次获取都.length需要查看 DOM 以查看有多少匹配的元素。此外,如果gEBCN在 IE8 中使用了 shim ,您将有一个无限循环,因为该列表不会生效。
2021-05-02 11:53:26
@squint 是的,你说得对!哇,我很高兴我现在不必经常使用 IE 了 :-)
2021-05-05 11:53:26
不是吹毛求疵,但您.slice()将在 IE8 及更低版本中失败,因为elementArray它是宿主对象。IMO,我只是做一个反向迭代。它对实时和非实时列表的工作方式相同,并且浏览器兼容且速度快。
2021-05-18 11:53:26

大多数返回元素列表的 DOM 函数返回的是 NodeList 而不是数组。最大的区别是 NodeList 通常是live,这意味着更改文档会导致节点神奇地出现或消失。这可能会导致节点稍微移动,并抛出一个不考虑它的循环。

但是,您可以简单地在返回的列表上向后循环,而不是将列表转换为数组或其他任何内容。

function example()
{
    var elements = document.getElementsByClassName("exampleClass");

    for(var i = elements.length - 1; i >= 0; --i)
    {
        // PERFORM STUFF ON THE ELEMENT
        elements[i].className = "exampleClassComplete";

        // elements[i] no longer exists past this point, in most browsers
    }   
}

NodeList中的活跃度不会不管在这一点上,因为从它删除的唯一要素将是那些以后你目前是我的唯一。在它之前出现的节点不会受到影响。

@Xotic750:当我之前遇到这个问题时,这对我来说似乎是自然的解决方案。当我查看答案并注意到大多数人都在放屁时……我不得不插话。:)
2021-04-26 11:53:26
+1 用于反向循环,如@Pointy 答案的评论中所述
2021-04-27 11:53:26
是的,一个非常简单的解决方案来更改活动节点列表,您的更改可能会影响列表:)
2021-05-14 11:53:26

另一种方法是使用选择器:

var arr = document.querySelectorAll('.exampleClass');
for (var i=0;i<arr.length;i++) {
    arr.innerHTML = "new value";
}

虽然它与旧浏览器不兼容,但如果 web 内容适用于现代浏览器,它也可以做到这一点。

哦,刚看到。我将删除“不建议”;-)
2021-04-25 11:53:26
有人建议,但仅作为@Pointy 答案下的评论
2021-05-19 11:53:26

您还可以使用 2 个数组,将数据推送到第一个数组中,然后执行您需要执行的操作 [例如更改元素的类]:

function example()
{
    var elementArray=[];
    var elementsToBeChanged=[];
    var i=0;

    elementArray = document.getElementsByClassName("exampleClass");

    for( i = 0; i < elementArray.length; i++){
        elementsToBeChanged.push(elementArray[i]);
    }

    for( i=0; i< elementsToBeChanged.length; i++)
    {
        elementsToBeChanged[i].setAttribute("class", "exampleClassComplete");
    }
}

另一种可能性,也应该是跨浏览器的,使用手工定制的 getElementsByClassName ,它将固定节点列表作为数组返回。这应该支持 IE5.5 及更高版本。

function getElementsByClassName(node, className) {
    var array = [],
        regex = new RegExp("(^| )" + className + "( |$)"),
        elements = node.getElementsByTagName("*"),
        length = elements.length,
        i = 0,
        element;

    while (i < length) {
        element = elements[i];
        if (regex.test(element.className)) {
            array.push(element);
        }

        i += 1;
    }

    return array;
}