I am running into trouble with this script below, as I take a collection based on a class name and then modify the class names of each member of the collection respectively the programs triggers that the very items of the collection are undefined.
I assume this has to do with the collection falling apart as I remove what made the elements belong to the collection? Could you give me a hint to how I could implement smth like this?
<script>
var toggle1 = document.getElementById("toggle1");
toggle1.addEventListener("click", Colortoggle);
function Colortoggle() {
if (toggle1.className === " white") {
var collection = document.getElementsByClassName("white");
for (let i = 0; i < collection.length; i++) {
collection[i].className -= "white";
collection[i].className += "black";
}
} else {
var collection = document.getElementsByClassName("black");
for (let i = 0; i < collection.length; i++) {
collection[i].className -= "black";
collection[i].className += "white";
}
}
console.log("colortoggle was finished");
}
</script>
The main logic for below code is toggle1.classList.contains("white") this statement return true if toggle has class white else return false.
WIth DOM elements you can use this classList methods classList.add(),classList.remove().
classList.add() => this will add class to selected DOM element.
classList.remove() => this will remove class to selected DOM element.
for more information abslout classList method please visit this.
var toggle1 = document.querySelector("#toggle1");
toggle1.addEventListener("click", Colortoggle);
function Colortoggle() {
if (toggle1.classList.contains("white")) {
var collection = document.querySelectorAll(".white");
collection.forEach(element => {
element.classList.add("black");
element.classList.remove("white");
})
} else {
var collection = document.querySelectorAll(".black");
collection.forEach(element => {
element.classList.add("white");
element.classList.remove("black");
})
}
console.log("colortoggle was finished");
}
.main div {
display: inline-block;
height: 100px;
width: 100px;
}
.white {
background-color: #f0f0f0;
color: black;
}
.black {
background-color: #000000;
color: white;
}
<div class="main">
<div id="toggle1" class="white">toggle1 click here</div>
<div class="white"></div>
<div class="white"></div>
<div class="black"></div>
<div class="black"></div>
</div>
As a sidenote: getElementsByClassName() returns a live HTMLCollection, which means you should never iterate over it using a for-loop while also modifying elements such as that they get removed from the collection.
Lets picture this:
Let this be the collection getElementsByClassName('foo'), with -> displaying the current iteration of the loop.
// list before iterating
0: <div class="foo">0</div>
1: <div class="foo">1</div>
2: <div class="foo">2</div>
// first iteration i=0
-> 0: <div class="foo">0</div> // in this iteration, remove .foo
1: <div class="foo">1</div>
2: <div class="foo">2</div>
// still first iteration, but now collection[0] is actually what was collection[1] before
-> 0: <div class="foo">1</div>
1: <div class="foo">2</div>
// second iteration i=1
0: <div class="foo">1</div> // we never touch this one!!
-> 1: <div class="foo">2</div>
You will not have this problem if instead of getElementsByClassName('foo') you work with querySelectorAll('.foo').
Second sidenote: No, this cannot be fixed using for (const div of divs) instead, as it internally seems to work with indexes as well.