JavaScript Toggle not working with many elements with the same class attribute

Viewed 255

I am trying to toggle 'disable' for 'active' CSS class to make the fill property of some SVG change when it is clicked.

I am able to make the first element change correctly but when trying the same with the second and third SVGs it changes the color of the first one inside the first div.

HTML

   <div>
       <svg onclick="toggleColor()" class="home__like disable heart">
           <use xlink:href="img/sprite.svg#icon-heart-full"></use>
       </svg>
   </div>
   <div>
       <svg onclick="toggleColor()" class="home__like disable heart">
           <use xlink:href="img/sprite.svg#icon-heart-full"></use>
       </svg>
   </div>
   <div>
       <svg onclick="toggleColor()" class="home__like disable heart">
           <use xlink:href="img/sprite.svg#icon-heart-full"></use>
       </svg>
   </div>

CSS

.disable {
    fill: #fff;
}

.active {
    fill: $color-primary;
}

JavaScript

function toggleColor() {

    const toggleHeart = document.querySelector('.heart');

    if(toggleHeart.classList.contains('disable')) {
        toggleHeart.classList.remove('disable');
        toggleHeart.classList.add('active');
    } else {
        toggleHeart.classList.remove('active');
        toggleHeart.classList.add('disable');
    }
}
2 Answers

You're using .querySelector() which returns the first element in the document that matches the selector provided.

To get multiple elements, you'd need to use .querySelectorAll() which will return a static NodeList of elements that match the selector. At that point you need to loop through the NodeList and manipulate the classes.

However, since you're trying to target the event-invoking element, I think you could simplify it with a reference to the element.

function toggleColor(el, e) {
    el.classList.toggle('disable');
    el.classList.toggle('active');
}

And change your onclick handler to onclick="toggleColor(this,event);"

Here's a quick snippet:

function toggleColor(el, e) {
    el.classList.toggle('disable');
    el.classList.toggle('active');
}
.disable { color: #ccc }
.active  { color: #0095ee; }
<div>
    <svg class="disable" onclick="toggleColor(this,event);" viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="2" fill="currentColor" stroke-linecap="round" stroke-linejoin="round" class="css-i6dzq1"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path></svg>
 </div>
 <div>
     <svg class="disable" onclick="toggleColor(this,event);" viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="2" fill="currentColor" stroke-linecap="round" stroke-linejoin="round" class="css-i6dzq1"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path></svg>
 </div>
 <div>
     <svg class="disable" onclick="toggleColor(this,event);" viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="2" fill="currentColor" stroke-linecap="round" stroke-linejoin="round" class="css-i6dzq1"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path></svg>
 </div>

Why is this happening?

Since there are multiple elements with the class of heart, it doesn't know which element to perform the script on! It's like a teacher trying to call Bob, when the class contains 5 people named Bob.

How can we fix this?

You can change the function in onclick, by supplying the element's reference in the script, like so: onclick="myFunction(this)". Now in the javascript, you can do whatever you need with the element.

Try it! Run the example below!

function toggleColor(element) {

    toggleHeart = element;

    if(toggleHeart.classList.contains('disable')) {
        toggleHeart.classList.remove('disable');
        toggleHeart.classList.add('active');
    } else {
        toggleHeart.classList.remove('active');
        toggleHeart.classList.add('disable');
    }
}
.disable {
    color: blue;
}

.active {
    color: red;
}
<!--                      vvv  below we used the "this" keyword which references to the svg element -->
<div onclick="toggleColor(this)" class="disable">
    Click Me!
</div>
<div onclick="toggleColor(this)" class="disable">
    Click Me!
</div>
<div onclick="toggleColor(this)" class="disable">
    Click Me!
</div>
<div onclick="toggleColor(this)" class="disable">
    Click Me!
</div>

Related