Is there a way to add/remove several classes in one single instruction with classList?

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.

Answers:

Answer

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) {...}
Answer

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']
);
Answer

To add class to a element

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

UPDATE:

Remove a class

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

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.

Answer

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

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

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

Or

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

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

Answer

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

Answer

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

Answer

You can do like below

Add

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

Remove

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

Reference

TLDR;

In straight forward case above removal should work. But in case of removal, you should make sure class exists before you remove them

const classes = ["first","second","third"];
classes.forEach(c => {
  if (elem.classList.contains(c)) {
     element.classList.remove(c);
  }
})
Answer

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);

Answer

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?

Answer

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.

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

is equal

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

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

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

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.