362

So far I have to do this:

elem.classList.add("first");
elem.classList.add("second");
elem.classList.add("third");

While this is doable in jQuery, like this

$(elem).addClass("first second third");

I'd like to know if there's any native way to add or remove.

17 Answers 17

709
elem.classList.add("first");
elem.classList.add("second");
elem.classList.add("third");

is equal to:

elem.classList.add("first", "second", "third");
Sign up to request clarification or add additional context in comments.

7 Comments

And if you want to apply an Array of several class-names, you have to call: DOMTokenList.prototype.add.apply(elem.classList, ['first', 'second', 'third']);
Firefox doesn't support it either, at the time of writing. My answer provides a polyfill.
Have you considered el.className += " my classes here"
is there a way to remove multiple classes
@Vicky elem.classList.remove("first","second","third"); Both add and remove methods are based on the same DOMTokenList.
|
166

The new spread operator makes it even easier to apply multiple CSS classes as array:

const list = ['first', 'second', 'third'];
element.classList.add(...list);

3 Comments

The accepted answer works with a direct list of values, but this one works with arrays... both seem excellent answers!
Can I use a DOMTokenList instead of an array? I know that a DOMTokenList is an array-like object. So it is possible to use it ith the classList.add() method or must the DOMTokenList be converted in a real array?
This is what I was looking for. If I have a list of classnames in a string like classnames = "class1 word bold px4", this is what I needed: element.classList.add(...classnames.split(' '))
81

You can do like below

Add

elem.classList.add("first", "second", "third");

// OR

elem.classList.add(...["first","second","third"]);

Remove

elem.classList.remove("first", "second", "third");

// OR

elem.classList.remove(...["first","second","third"]);

Reference

You don't need to check the existence first. The docs say: "If the string is not in the list, no error is thrown, and nothing happens."

1 Comment

You don't need to check the existence first. The docs say: "If the string is not in the list, no error is thrown, and nothing happens."
24

The classList property ensures that duplicate classes are not unnecessarily added to the element. In order to keep this functionality, if you dislike the longhand versions or jQuery version, I'd suggest adding an addMany function and removeMany to DOMTokenList (the type of classList):

DOMTokenList.prototype.addMany = function(classes) {
    var array = classes.split(' ');
    for (var i = 0, length = array.length; i < length; i++) {
      this.add(array[i]);
    }
}

DOMTokenList.prototype.removeMany = function(classes) {
    var array = classes.split(' ');
    for (var i = 0, length = array.length; i < length; i++) {
      this.remove(array[i]);
    }
}

These would then be useable like so:

elem.classList.addMany("first second third");
elem.classList.removeMany("first third");

Update

As per your comments, if you wish to only write a custom method for these in the event they are not defined, try the following:

DOMTokenList.prototype.addMany = DOMTokenList.prototype.addMany || function(classes) {...}
DOMTokenList.prototype.removeMany = DOMTokenList.prototype.removeMany || function(classes) {...}

Comments

21

Since the add() method from the classList just allows to pass separate arguments and not a single array, you need to invoque add() using apply. For the first argument you will need to pass the classList reference from the same DOM node and as a second argument the array of classes that you want to add:

element.classList.add.apply(
  element.classList,
  ['class-0', 'class-1', 'class-2']
);

1 Comment

It's ES5 version of solution with the Spread Operator (see @morkro answer). Check it easily at Babel Repl.
15

Here is a work around for IE 10 and 11 users that seemed pretty straight forward.

var elem = document.getElementById('elem');

['first','second','third'].forEach(item => elem.classList.add(item));
<div id="elem">Hello World!</div>

Or

var elem = document.getElementById('elem'),
    classes = ['first','second','third'];

classes.forEach(function(item) {
    elem.classList.add(item);
});
<div id="elem">Hello World!</div>

Comments

11

To add class to a element

document.querySelector(elem).className+=' first second third';

Remove a class

document.querySelector(elem).className=document.querySelector(elem).className.split(class_to_be_removed).join(" ");

2 Comments

This is interesting to know, but all it does is to edit the string of the class. It wouldnt work if I wanted to remove several classes. Sorry, I forgot to add that in the OP.
Do NOT use className. It is terrible for performance (even retrieving its value causes a reflow).
7

Newer versions of the DOMTokenList spec allow for multiple arguments to add() and remove(), as well as a second argument to toggle() to force state.

At the time of writing, Chrome supports multiple arguments to add() and remove(), but none of the other browsers do. IE 10 and lower, Firefox 23 and lower, Chrome 23 and lower and other browsers do not support the second argument to toggle().

I wrote the following small polyfill to tide me over until support expands:

(function () {
    /*global DOMTokenList */
    var dummy  = document.createElement('div'),
        dtp    = DOMTokenList.prototype,
        toggle = dtp.toggle,
        add    = dtp.add,
        rem    = dtp.remove;

    dummy.classList.add('class1', 'class2');

    // Older versions of the HTMLElement.classList spec didn't allow multiple
    // arguments, easy to test for
    if (!dummy.classList.contains('class2')) {
        dtp.add    = function () {
            Array.prototype.forEach.call(arguments, add.bind(this));
        };
        dtp.remove = function () {
            Array.prototype.forEach.call(arguments, rem.bind(this));
        };
    }

    // Older versions of the spec didn't have a forcedState argument for
    // `toggle` either, test by checking the return value after forcing
    if (!dummy.classList.toggle('class1', true)) {
        dtp.toggle = function (cls, forcedState) {
            if (forcedState === undefined)
                return toggle.call(this, cls);

            (forcedState ? add : rem).call(this, cls);
            return !!forcedState;
        };
    }
})();

A modern browser with ES5 compliance and DOMTokenList are expected, but I'm using this polyfill in several specifically targeted environments, so it works great for me, but it might need tweaking for scripts that will run in legacy browser environments such as IE 8 and lower.

3 Comments

Nobody seems to mention that IE10 doesn't allow for multiple classes to be added. This worked nicely for me so thanks! I would caution though that in IE9 I get an error that DomTokenList is not defined. You may want to account for that too. Or only run this if( typeof DomTokenList !== 'undefined')
@Ryan: thanks, I thought DOMTokenList was supported in IE 9 but I guess it was missed. I'll try and investigate a workaround at some point.
Looks like on a failed DOMTokenList you'd have to use className with word boundary regEx checks
3

A very simple, non fancy, but working solution that I would have to believe is very cross browser:

Create this function

function removeAddClasses(classList,removeCollection,addCollection){
    for (var i=0;i<removeCollection.length;i++){ 
        classList.remove(removeCollection[i]); 
    }
    for (var i=0;i<addCollection.length;i++){ 
        classList.add(addCollection[i]); 
    }
}

Call it like this: removeAddClasses(node.classList,arrayToRemove,arrayToAdd);

...where arrayToRemove is an array of class names to remove: ['myClass1','myClass2'] etcetera

...and arrayToAdd is an array of class names to add: ['myClass3','myClass4'] etcetera

Comments

3

A better way to add the multiple classes separated by spaces in a string is using the Spread_syntax with the split:

element.classList.add(...classesStr.split(" "));

1 Comment

If you split(/\s+/g) instead, then you can have multiple spaces, which is useful if your input may not be well formed
3

I found a very simple method which is more modern and elegant way.


const el = document.querySelector('.m-element');

// To toggle
['class1', 'class2'].map((e) => el.classList.toggle(e));

// To add
['class1', 'class2'].map((e) => el.classList.add(e));

// To remove
['class1', 'class2'].map((e) => el.classList.remove(e));

Good thing is you can extend the class array or use any coming from API easily.

1 Comment

Prefer forEach over map when you're ignoring the return value.
2

The standard definiton allows only for adding or deleting a single class. A couple of small wrapper functions can do what you ask :

function addClasses (el, classes) {
  classes = Array.prototype.slice.call (arguments, 1);
  console.log (classes);
  for (var i = classes.length; i--;) {
    classes[i] = classes[i].trim ().split (/\s*,\s*|\s+/);
    for (var j = classes[i].length; j--;)
      el.classList.add (classes[i][j]);
  }
}

function removeClasses (el, classes) {
  classes = Array.prototype.slice.call (arguments, 1);
  for (var i = classes.length; i--;) {
    classes[i] = classes[i].trim ().split (/\s*,\s*|\s+/);
    for (var j = classes[i].length; j--;)
      el.classList.remove (classes[i][j]);
  }
}

These wrappers allow you to specify the list of classes as separate arguments, as strings with space or comma separated items, or a combination. For an example see http://jsfiddle.net/jstoolsmith/eCqy7

Comments

2

Assume that you have an array of classes to being added, you can use ES6 spread syntax:

let classes = ['first', 'second', 'third']; elem.classList.add(...classes);

Comments

0

Another polyfill for element.classList is here. I found it via MDN.

I include that script and use element.classList.add("first","second","third") as it's intended.

Comments

0

One of the best solution is to check if an element exists and then proceed to add or possibly remove and above all if the element is empty, delete it.

/**
 * @description detect if obj is an element
 * @param {*} obj
 * @returns {Boolean}
 * @example
 * see below
 */
function isElement(obj) {
  if (typeof obj !== 'object') {
    return false
  }
  let prototypeStr, prototype
  do {
    prototype = Object.getPrototypeOf(obj)
    // to work in iframe
    prototypeStr = Object.prototype.toString.call(prototype)
    // '[object Document]' is used to detect document
    if (
      prototypeStr === '[object Element]' ||
      prototypeStr === '[object Document]'
    ) {
      return true
    }
    obj = prototype
    // null is the terminal of object
  } while (prototype !== null)
    return false
}

/*
 * Add multiple class
 * addClasses(element,['class1','class2','class3'])
 * el: element | document.querySelector(".mydiv");
 * classes: passing:: array or string : [] | 'cl1,cl2' | 'cl1 cl2' | 'cl1|cl2'
 */
function addClasses(el, classes) {
  classes = Array.prototype.slice.call(arguments, 1);
  if ( isElement(el) ){ //if (document.body.contains(el)
    for (var i = classes.length; i--;) {
      classes[i] = Array.isArray(classes[i]) ? classes[i]: classes[i].trim().split(/\s*,\s*|\s+/);
      for (var j = classes[i].length; j--;)
        el.classList.add(classes[i][j]);
    }
  }
}

/*
 * Remove multiple class
 * Remove attribute class is empty
 * addClasses(element,['class1','class2','class3'])
 * el: element | document.querySelector(".mydiv");
 * classes: passing:: array or string : [] | 'cl1,cl2' | 'cl1 cl2' | 'cl1|cl2'
 */
function removeClasses(el, classes) {
  classes = Array.prototype.slice.call(arguments, 1);
  if ( isElement(el) ) {
    for (var i = classes.length; i--;) {
      classes[i] = Array.isArray(classes[i]) ? classes[i]: classes[i].trim().split(/\s*,\s*|\s+/);
      for (var j = classes[i].length; j--;)
        el.classList.remove(classes[i][j]);
        let cl = el.className.trim();
        if (!cl){
          el.removeAttribute('class');
        }
    }
  }
}

var div = document.createElement("div");
div.id = 'test'; // div.setAttribute("id", "test");
div.textContent = 'The World';
//div.className = 'class';
// possible use: afterend, beforeend
document.body.insertAdjacentElement('beforeend', div); 

// Everything written above you can do so:
//document.body.insertAdjacentHTML('beforeend', '<div id="text"></div>');

var el = document.querySelector("#test");
addClasses(el,'one,two,three,four');
removeClasses(el,'two,two,never,other');
el.innerHTML = 'I found: '+el.className;
// return : I found: four three one
#test {
  display: inline-block;
  border: 1px solid silver;
  padding: 10px;
}

Comments

0

Create an array with the classes you need:

const classArray = ['class1', 'class2'];

Then call the add() method from classList using the spread operator to add all classes at once:

element.classList.add(...classArray);

Comments

-1

I liked @rich.kelly's answer, but I wanted to use the same nomenclature as classList.add() (comma seperated strings), so a slight deviation.

DOMTokenList.prototype.addMany = DOMTokenList.prototype.addMany || function() {
  for (var i = 0; i < arguments.length; i++) {
    this.add(arguments[i]);
  }
}
DOMTokenList.prototype.removeMany = DOMTokenList.prototype.removeMany || function() {
  for (var i = 0; i < arguments.length; i++) {
    this.remove(arguments[i]);
  }
}

So you can then use:

document.body.classList.addMany('class-one','class-two','class-three');

I need to test all browsers, but this worked for Chrome.
Should we be checking for something more specific than the existence of DOMTokenList.prototype.addMany? What exactly causes classList.add() to fail in IE11?

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.