className only changing every other class
I'm performing a small text with JavaScript with the getElementsByClassName()
and I am getting some unwanted results. I would like the script to change each CSS class to a new class. The issue is that every other class is only changing...
I would like to use pure js how this issue as it is for practice purposes.
The first thing that came to mind was white spaces, although when removing the this did not make any differnce.
Can anyone point our what I am doing wrong?
<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 Classes:
.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;
}
Because you change the .className
of the blockSet
which is an HTMLCollection
. The collection that have elements with same class (block-default
) will change when the elements suffers some updates.
In other words when you change the .className
of an element the collection will exclude that element. This means that the size of the HTMLCollection
will decrease . Also the size will increase if an element with that class has beed added to the DOM.
To solve this you can always change only the first element .className
.
for(var i = 0; i<blockSetLength; i++)
{
blockSet[0].className = "block-selected";
}
Notes: Intead of changing class element by element, you can iterate through elements with for
and change .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>
If you add a new item in DOM (not collection) the size will increase as presented in the example below.
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>
Instead of using getElementsByClassName()
,
which returns a live HTMLCollection that will change as the className
s change,
you can use querySelectorAll()
,
which returns a non-live NodeList that will not change.
querySelectorAll()
has better IE support than getElementsByClassName()
(IE8+ vs. IE9+).
It's also much more flexible since it supports CSS selectors (CSS2 for IE8+ and CSS3 for IE9+).
However, querySelectorAll()
is slower than getElementsByClassName()
.
Keep that in mind if you're processing thousands of DOM elements.
Snippet
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>
By assigning a value to .className
you overwrite every class on that element.
What you might want to take a look at is the .classList
attribute.
Remove a class:
blockSet[0].classList.remove('block-default');
Add the new class:
blockSet[0].classList.add('block-selected');
A good point to start with, when your trying to do stuff, jQuery usually did for you, is http://youmightnotneedjquery.com/
You have already some good solutions.
I think that the best one is the one from Rick Hitchcock.
But a solution that I often use, to be safe when doing things like that, is to travel the collection backwards
var nmax = blockSet.length - 1;
for (var n=nmax; n>=0; n--) {
blockSet[n].className = 'block-selected';
}
That isolates you from changes in the collection