Your reuse of the i
variable within the event callback isn't referencing the element as you think it is, therefore causing the error.
This all stems from the way you are attempting to loop over the selected elements and how you are attempting to access them when you loop. getElementsByClassName()
should rarely (if ever) be used and most certainly not if you are going to loop over the returned node list.
And, if you use the correct modern approach to getting the references, you can then use the modern .forEach()
method to iterate over the node list.
Then, during each iteration, when you set up the event handler, you can refer to the current item with the argument specified within the callback function of the forEach()
method.
Finally, within the event handler, you can refer to the element that triggered the event in the first place with the this
identifier.
Here would be the modern way to do this:
// No need to set up a variable to reference the selected nodes if you
// don't plan to reference the entire node list again. Instead, you can
// just "chain" the .forEach() method on the resulting node list.
// Note that forEach() specifies (with the first argument) the identifier
// that each item in the node list should be accessible as (element in this case):
document.querySelectorAll(".item").forEach(function(element){
// Then you can access each item during the loop with the variable from forEach():
element.addEventListener("click", function(event){
// And within the event handler, this will refer to the element that triggered
// the event in the first place. Using this, you can access properties of the element
// directly with "dot notation".
this.classList.add("active");
// Just for fun:
console.log("You initiated the " + event.type + " event of the " + this + " element, which has the: " + this.className + " class(es).");
});
});
.active { color:red; }
<ul>
<li class="item">test</li>
<li class="item">test</li>
<li class="item">test</li>
<li class="item">test</li>
<li class="item">test</li>
</ul>
And, if you are wanting only the most recently clicked item to have the active class, you need to loop over all the items upon the click of any of them and remove the class first. Then, you can apply it to only the one that was just clicked. That would look like this:
let items = document.querySelectorAll(".item");
items.forEach(function(element){
element.addEventListener("click", function(event){
// First, loop over all the node list elements and remove the active class
items.forEach(function(item){
item.classList.remove("active");
});
// The apply the active class to only the one that just got clicked:
this.classList.add("active");
});
});
.active { color:red; }
<ul>
<li class="item">test</li>
<li class="item">test</li>
<li class="item">test</li>
<li class="item">test</li>
<li class="item">test</li>
</ul>