How to remove a class from elements in pure JavaScript?
Asked Answered
S

9

114

I would like to know how to select all elements with class names "widget" and "hover" and then remove class "hover" from these elements.

I have the following JavaScript code that selects all elements with class "widget" and "hover":

var elements = document.getElementsByClassName('widget hover');
console.log(elements);

This seems to work and outputs something like this (with no errors):

[div#.widget... 

The problem is that if I try to remove the class "hover", I get an error:

var elements = document.getElementsByClassName('widget hover');
console.log(elements);
elements.classList.remove("hover");

This outputs:

[item: function]
length: 0
Uncaught TypeError: Cannot call method 'remove' of undefined 

Can anyone tell me what I'm doing wrong?


Please note that I have it working in jQuery:

$('.widget.hover').removeClass('hover');

... but I'm looking for a solution in pure JavaScript.

Sorcim answered 8/3, 2014 at 14:40 Comment(2)
Note that classList will not work in IE <= 9.Doubling
I don't care about IE8 and under but what would one use for IE9?Sorcim
E
147
var elems = document.querySelectorAll(".widget.hover");

[].forEach.call(elems, function(el) {
    el.classList.remove("hover");
});

You can patch .classList into IE9. Otherwise, you'll need to modify the .className.

var elems = document.querySelectorAll(".widget.hover");

[].forEach.call(elems, function(el) {
    el.className = el.className.replace(/\bhover\b/, "");
});

The .forEach() also needs a patch for IE8, but that's pretty common anyway.

Electrolytic answered 8/3, 2014 at 14:44 Comment(3)
@Andrew: The .className.replace() should be a little more sophisticated if the classes may have other than alpha-numeric characters. For example, if a class could be hover-me, the hover part could possibly be matched by the regex. Not sure how broad of a solution you need.Electrolytic
Because you were the first to answer, I'll give it you.Sorcim
@Andrew: I wasn't first, but the other answer does need to be fixed.Electrolytic
G
71

Modern solution... keep it simple and use es6

Times have changed and now the cleanest and most readable way to do this is:

Array.from(document.querySelectorAll('.widget.hover')).forEach(
  (el) => el.classList.remove('hover')
);

If you can't support arrow functions then just convert it like this:

Array.from(document.querySelectorAll('.widget.hover')).forEach(function(el) { 
    el.classList.remove('hover');
});

Additionally if you need to support extremely old browsers then use a polyfil for the forEach and Array.from and move on with your life.

Glassy answered 13/5, 2020 at 11:40 Comment(5)
Wouldn't a querySelectorAll be a more modern way of doing this?Gentlemanfarmer
@Gentlemanfarmer I should have emphasized the point of mine was not how you get the elements, rather how you are looping through and removing them using Array.from and forEachGlassy
you could get ride of Array.from: (document.querySelectorAll("div")).forEach((el) => el.classList.remove("selected"));Recalesce
I tried without the "Array.from" and it seems to be working too, please let me know if there are any special purpose for using "Array.from".Dropping
@Dropping - that would still work, the difference between the two is that one references a NodeList[] and the Array.from references an Array[], see this writeup for more information.Glassy
H
42

Find elements:

var elements = document.getElementsByClassName('widget hover');

Since elements is a live array and reflects all dom changes you can remove all hover classes with a simple while loop:

while(elements.length > 0){
    elements[0].classList.remove('hover');
}
However answered 3/8, 2016 at 10:24 Comment(1)
i like this, but don't you need a for loop here ?Osborne
W
12

Elements is an array of DOM objects. You should do something like this:

for (var i = 0; i < elements.length; i++) {
   elements[i].classList.remove('hover');
}

Enumerate the elements collection and for each element inside the collection call the remove method

Warmongering answered 8/3, 2014 at 14:42 Comment(5)
This works perfectly. I didn't realize that elements would be an array so this makes perfect sense. Is there anything faster that wouldn't have to re-iterate through the entire array?Sorcim
I believe there isn't anything faster. JQuery probably should do something similar. But don't worry about speed you must have thousands(if not million) of elements in order to see a performance problemWarmongering
One thing to consider is that in the question, .getElementsByClassName was being used to fetch the elements. Because you're removing one of the classes used for the fetch, the element will be removed from the elements list, which means the list gets re-indexed, and you end up skipping over some elements. It's safer to iterate in reverse.Electrolytic
Based on @cookiemonster's point, here's the recursive version: for (var i = elements.length-1; i > -1; i--) {}.Dumfries
@MattBaker your version works but not because it is recursive version, it only works because getElementsByClassName returns "live" array so when you go from 0-up as you remove class they disappear from "live" array. When you hide 0th item then 1st item move to 0th position but you continue for cycle from item 1, thus leaving 0th item with class. Going from end solves this issue.Nitrosyl
F
8

For ES6, this can be done in a few ways with one liners, where you create an array of the elements with the spread operator ..., and remove the class with the map operator:

With querySelectorAll:

[...document.querySelectorAll('.widget')].map(x => x.classList.remove('hover'));

With getElementsByClassName:

[...document.getElementsByClassName('widget')].map(x => x.classList.remove('hover'));

For querySelectorAll, notice the use of .widget instead of widget. An alternative for the spread operator would be to use Array.from like:

Array.from(document.querySelectorAll('.widget')).map(x => x.classList.remove('hover'));
Fraze answered 1/3, 2021 at 22:40 Comment(0)
D
5

This might help:

let allElements = Array.from(document.querySelectorAll('.widget.hover'));
for (let element of allElements) {
  element.classList.remove('hover');
}
Dog answered 4/5, 2018 at 12:52 Comment(0)
B
2

I use a simple method. If you always process [0] in the required number of loops, you can apply the process to all.

The HTMLCollection(elements) changes in real time, so put the length in a variable. (l = element.length)

for(var elements = document.getElementsByClassName('widget hover'), i = 0, l = elements.length; l > i; i++) {
  elements[0].classList.remove("hover");
}
Budde answered 15/7, 2021 at 2:36 Comment(0)
P
1
var elems = document.querySelectorAll(".widget.hover");

 for(let elem of elems){
        elem.classList.remove('hover');
        }
Pycnometer answered 26/4, 2021 at 7:47 Comment(0)
C
0

Given worked for me.

document.querySelectorAll(".widget.hover").forEach(obj=>obj.classList.remove("hover"));
Celisse answered 5/9, 2020 at 14:13 Comment(1)
Welcome to Stack Overflow! In order to maintain a high quality of answers, avoid answers that are only code and add explanations.Consecutive

© 2022 - 2024 — McMap. All rights reserved.