类名只更改每隔一个类

25

我正在使用JavaScript和getElementsByClassName()执行一些小文本,并且得到了一些不需要的结果。我想要脚本将每个CSS类更改为新类。问题是,每隔一个类只会更改...

出于练习目的,我想使用纯JS来解决这个问题。

首先想到的是空格,但是去掉空格后并没有任何区别。

有人能指出我做错了什么吗?

<html>
    <head>
       <link rel="stylesheet" type="text/css" href="default.css">
    </head>
    <body>
        <div class="block-default">BLOCK1</div>
        <div class="block-default">BLOCK2</div>
        <div class="block-default">BLOCK3</div>
        <div class="block-default">BLOCK4</div>
        <div class="block-default">BLOCK5</div>
        <div class="block-default">BLOCK6</div>
        <div class="block-default">BLOCK7</div>
        <div class="block-default">BLOCK8</div>
        <script>

    var blockSet = document.getElementsByClassName("block-default");
    var blockSetLength = blockSet.length;

    blockSet[0].className = "block-selected";
    blockSet[1].className = "block-selected";
    blockSet[2].className = "block-selected";
    blockSet[3].className = "block-selected";
    blockSet[4].className = "block-selected";
    blockSet[5].className = "block-selected";
    blockSet[6].className = "block-selected";
    blockSet[7].className = "block-selected";   

        </script>
    </body>
</html>

CSS 类:

.block-default {
    width: 100px;
    height:50px;
    background-color: green;
    border: 1px solid red;
    padding:10px;
}

.block-selected {
    width: 100px;
    height:50px;
    background-color: blue;
    border: 1px solid white;
    padding:10px;
 }

如果你想保留你的代码,你可以将它下降而不是上升。从blockSet[7]开始,一直下降到blockSet[0]。 - Lain
2
这实际上是一个与https://dev59.com/M2Qn5IYBdhLWcg3w36Q1重复的问题。 - Drakes
请简要说明当您想更改类时以及将更改哪个元素类,例如如果您想在单击事件上更改类? - Mitul
blockSetLength 是用来做什么的? - yaakov
1
这是多个问题的重复:12345。你在提问之前进行了研究吗? - Anonymous
16个回答

22
因为您更改了blockSet.className,它是一个HTMLCollection。具有相同类(block-default)的元素的集合将在元素遭受某些更新时发生更改。
换句话说,当您更改元素的.className时,集合将排除该元素。这意味着HTMLCollection大小将减小。如果具有该类的元素已添加到DOM中,则大小将增加
要解决此问题,您可以始终仅更改第一个元素的.className
for(var i = 0; i<blockSetLength; i++)
{
    blockSet[0].className = "block-selected";
}

注意:不必逐个更改类元素,您可以通过使用for遍历元素并更改.className

var blockSet = document.getElementsByClassName("block-default");
var blockSetLength = blockSet.length;

console.log(blockSet);

for(var i = 0; i<blockSetLength; i++)
{
    blockSet[0].className = "block-selected";
}
.block-default {
    width: 100px;
    height:50px;
    background-color: green;
    border: 1px solid red;
    padding:10px;
}

.block-selected {
    width: 100px;
    height:50px;
    background-color: blue;
    border: 1px solid white;
    padding:10px;
 }
<div class="block-default">BLOCK1</div>
<div class="block-default">BLOCK2</div>
<div class="block-default">BLOCK3</div>
<div class="block-default">BLOCK4</div>
<div class="block-default">BLOCK5</div>
<div class="block-default">BLOCK6</div>
<div class="block-default">BLOCK7</div>
<div class="block-default">BLOCK8</div>

如果您在DOM中添加一个新项(而不是集合),则大小将增加,如下面的示例所示。

var blockSet = document.getElementsByClassName("block-default");
var blockSetLength = blockSet.length;

alert("Current size: " + blockSet.length);
document.body.innerHTML += '<div class="block-default">BLOCK9</div>';
alert("After adding an element in DOM size: " + blockSet.length);
.block-default {
    width: 100px;
    height:50px;
    background-color: green;
    border: 1px solid red;
    padding:10px;
}

.block-selected {
    width: 100px;
    height:50px;
    background-color: blue;
    border: 1px solid white;
    padding:10px;
 }
<div class="block-default">BLOCK1</div>
<div class="block-default">BLOCK2</div>
<div class="block-default">BLOCK3</div>
<div class="block-default">BLOCK4</div>
<div class="block-default">BLOCK5</div>
<div class="block-default">BLOCK6</div>
<div class="block-default">BLOCK7</div>
<div class="block-default">BLOCK8</div>


13

不要使用返回会随着 className 更改而变化的 活动 HTMLCollectiongetElementsByClassName()
而是可以使用返回 非活动 NodeListquerySelectorAll(),
该列表不会更改。

querySelectorAll()getElementsByClassName() 具有更好的 IE 支持 (IE8+ vs. IE9+)。
它也更加灵活,因为它支持 CSS 选择器(CSS2 适用于 IE8+,CSS3 适用于 IE9+)。

然而,querySelectorAll()getElementsByClassName()
如果您正在处理数千个 DOM 元素,请记住这一点。

片段

var blockSet = document.querySelectorAll(".block-default");
var blockSetLength = blockSet.length;

blockSet[0].className = "block-selected";
blockSet[1].className = "block-selected";
blockSet[2].className = "block-selected";
blockSet[3].className = "block-selected";
blockSet[4].className = "block-selected";
blockSet[5].className = "block-selected";
blockSet[6].className = "block-selected";
blockSet[7].className = "block-selected";
.block-default {
  width: 100px;
  height: 50px;
  background-color: green;
  border: 1px solid red;
  padding: 10px;
}
.block-selected {
  width: 100px;
  height: 50px;
  background-color: blue;
  border: 1px solid white;
  padding: 10px;
}
<div class="block-default">BLOCK1</div>
<div class="block-default">BLOCK2</div>
<div class="block-default">BLOCK3</div>
<div class="block-default">BLOCK4</div>
<div class="block-default">BLOCK5</div>
<div class="block-default">BLOCK6</div>
<div class="block-default">BLOCK7</div>
<div class="block-default">BLOCK8</div>


4
这可能是最安全的方法。所有其他解决方案都建议将实时集合中的第一项设置为默认值,但在某些极特殊的情况下仍可能存在问题。 - light
2
如果出于任何原因您不想使用 querySelectorAll,或者只是想要一种替代方案,那么可以使用 Array.from(liveNodeList)[].slice.call(liveNodeList)liveNodeList 转换为静态可变数组。 - user3459110

6
通过给.className赋值,您会覆盖该元素上的每个类。 您可能想要查看的是.classList属性。
删除一个类:
blockSet[0].classList.remove('block-default');

添加新类:

blockSet[0].classList.add('block-selected');

如果你想做一些jQuery通常为你完成的工作,一个好的起点是http://youmightnotneedjquery.com/

。该网站可以帮助你找到纯JavaScript实现相同功能的方法,让你的代码更加纯净并提升性能。


5

你已经有了一些好的解决方案。

我认为最好的一个是Rick Hitchcock的方案。

但是,我经常使用的一个解决方案,在进行这样的操作时更加安全,就是倒序遍历集合。

var nmax = blockSet.length - 1;
for (var n=nmax; n>=0; n--) {
    blockSet[n].className = 'block-selected';
}

这将使你与集合中的更改隔绝开来


这是最好的解决方案;关键在于使用 getElementsByClassName 时,您正在与一个实时数据结构交互,它会立即更新...向后循环不会影响您的索引。 - maioman

3

document.getElementsByClassName返回一个HTMLCollection对象,它是实时的

在HTML DOM中,HTMLCollection是实时的;当底层文档更改时,它会自动更新。

因此,当您调用

blockSet[0].className = "block-selected";

您更改了基础文档,因此该项不再在集合中(blockSet [0] 现在是原始选择中的第二项)。


1
使用 var blockSet = Array.slice.call(document.getElementsByClassName("block-default")); 将 HTML 集合转换为常规数组。 - HBP
没错。值得一提的是,当调用 $(".block-default") 时,jQuery 使用的是数组而不是 HTMLCollection,因此您无法使用该库实现此行为。 - Jan Turoň

2
这个错误是因为blockSet是一个“实时”的HTMLCollection。当页面上的元素更新时,HTMLCollections也会更新。

每次交换className时,你都会使blockSet逐渐缩短。

要解决这个问题,只需改成这样:

for (var i = 0; i < 8; i += 1) {
    blockSet[ 0 ].className = "block-selected";
}

这样你就可以逐个切分你的HTMLCollection

迭代1:[ div1, div2, div3, div4, div5, div6, div7, div8 ]

迭代2:[ div2, div3, div4, div5, div6, div7, div8 ]

迭代3:[ div3, div4, div5, div6, div7, div8 ]

迭代4:[ div4, div5, div6, div7, div8 ]

迭代5:[ div5, div6, div7, div8 ]

迭代6:[ div6, div7, div8 ]

迭代7:[ div7, div8 ]

迭代8:[ div8 ]

希望这有所帮助!


2
您可以使用以下代码最简单地实现此操作:
while(blockSetLength--){ 
    //this will change the class of n-1 dom object 
    blockSet[blockSetLength].className='block-selected';
}

1
这对我有用。

var blockSet = document.getElementsByClassName("block-default");
    var blockSetLength = blockSet.length;
 blockSet[0].className = "block-selected";
 blockSet[0].className = "block-selected";
 blockSet[0].className = "block-selected";
 blockSet[0].className = "block-selected";
 blockSet[0].className = "block-selected";
 blockSet[0].className = "block-selected";
 blockSet[0].className = "block-selected";
 blockSet[0].className = "block-selected";

   
.block-default {
    width: 100px;
    height:50px;
    background-color: green;
    border: 1px solid red;
    padding:10px;
}

.block-selected {
    width: 100px;
    height:50px;
    background-color: blue;
    border: 1px solid white;
    padding:10px;
 }
<div class ="block-default">BLOCK1</div>
<div class ="block-default">BLOCK2</div>
<div class ="block-default">BLOCK3</div>
<div class ="block-default">BLOCK4</div>
<div class ="block-default">BLOCK5</div>
<div class ="block-default">BLOCK6</div>
<div class ="block-default">BLOCK7</div>
<div class ="block-default">BLOCK8</div>


谢谢大家为我澄清了这个问题!因此,由于该进程是实时的,定位数组中的第一项将根据长度更改整个部分!感谢大家 - Ryan - EvilGenius82

0

这个错误是因为你需要执行

blockSet[0].className = 'block-selected'

你不需要写 blockSet[1],blockSet[2] ...

你可以这样做:

for (var s=0;s<blockSetLength;s++) {
  blockSet[0].className = 'block-selected';
}

你的回答与现有答案有何不同? - adricadar

0

你的错误是因为.className返回一个实时的HTMLCollection。所以当你做这样的事情时:

blockSet[0].className = "block-selected";

你的集合 blockSet[0] 将变为 blockSet[1]。因此,当你执行以下代码行时:

blockSet[1].className = "block-selected";

你没有改变你想要改变的blockSet[1],但是你改变了起始blockSet[2]

所以你可以这样做:

var blockSet = document.getElementsByClassName("block-default");
var blockSetLength = blockSet.length;
for(var i=0;i<blockSetLength;i++){
    blockSet[0].classList.add('block-selected'); //add the new class first
    blockSet[0].classList.remove('block-default'); //delete the old one
}

http://jsfiddle.net/94dqffa7/

我认为这是最好的方法。因为classList.add()classList.remove()可以帮助您在不删除div上其他类别的情况下更改类别。此外,您需要先添加新类别,再删除旧类别,否则你会遇到跟以前一样的问题。


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