39

I use these JavaScript-code to change classes in my script:

var toggleDirection = function() {
  group.classList.toggle('left-to-right');
  group.classList.toggle('right-to-left');
}

In my example there a only two classes to change but could be also multiple classes ...

So therefore: Does anyone know a way to write the example less redundant?

2

12 Answers 12

27

No it is not possible using Element.classList API directly. Looking at API you can read:

toggle ( String [, force] ) When only one argument is present: Toggle class value; i.e., if class exists then remove it, if not, then add it. When a second argument is present: If the second argument is true, add specified class value, and if it is false, remove it.

Reference here.

You could potentially write your own "utility" function (in vanilla JS) which does what you want, below a very simple demonstrative example which work on top of the classList API:

var superToggle = function(element, class0, class1) {
  element.classList.toggle(class0);
  element.classList.toggle(class1);
}

And you call it in this way:

superToggle(group,'left-to-right', 'right-to-left');
23

For anyone looking for a short answer, you can do this on one line using the rest parameter introduced in ES6/ES2015:

const toggleCSSclasses = (el, ...cls) => cls.map(cl => el.classList.toggle(cl))

This is pretty close to @attacomsian's answer, but taking advantage of the fact that the rest parameter will return an array - no matter how many arguments is being passed to the function. Which means we can skip the part where we detect whether we're working with a string or an array.

const toggleCSSclasses = (el, ...cls) => cls.map(cl => el.classList.toggle(cl));

const one = document.querySelector(".one");

one.addEventListener("click", () => {
  toggleCSSclasses(one, "class1");
});

const two = document.querySelector(".two");

two.addEventListener("click", () => {
  toggleCSSclasses(two, "class1", "class2");
});
.class1 { text-decoration: underline }
.class2 { background: steelblue }
<p class="one">Click to toggle one class</p>
<p class="two">Click to toggle two classes</p>

19

just use the map array.

like

['left-to-right', 'right-to-left'].map(v=> group.classList.toggle(v) )
4

Here is ES6 version of solution

const classToggle = (el, ...args) => args.map(e => el.classList.toggle(e))

const classToggle = (el, ...args) => {
  args.map(e => el.classList.toggle(e))
}
.a {
  color: red
}

.b {
  background: black
}

.c {
  border-color: yellow
}
<button onclick="classToggle(this,'a', 'c','b')" class="a b c ">Click me</button>

And here's old JS code:

var classToggle = function classToggle(el) {
  for (
    var _len = arguments.length,
      args = new Array(_len > 1 ? _len - 1 : 0),
      _key = 1;
    _key < _len;
    _key++
  ) {
    args[_key - 1] = arguments[_key];
  }

  args.map(function (e) {
    return el.classList.toggle(e);
  });
};
1
  • This looks like the simplest answer but the code snippet does not show show yellow, just goes white to red. Also is it possible to toggle multiple divs/items/button on the same click? Commented Aug 31, 2022 at 3:39
3

Answer from year 2020 here!

Found this article helpful from 4/2021

Can use comma separated list of classes like this:

const button = document.getElementById('button')

button.classList.add('btn', 'btn-primary', 'btn-primary--footer')
button.classList.remove('btn', 'btn-primary', 'btn-primary--footer')

or even spread syntax from a list of classes:

const button = document.getElementById('button')
const classes = ['btn', 'btn-primary', 'btn-primary--footer']

button.classList.add(...classes)
button.classList.remove(...classes)
1

You can extend the DOMTokenList object with the following multiToggle

if (window["DOMTokenList"]) //check if DOMTokenList is an existing object.
{
//multitoggle
DOMTokenList.prototype.multiToggle = function()
{
    if (arguments.length > 0) // there needs to be at least one object
    {
        for (argument in arguments) //loop all arguments
        {
            var argument = arguments[argument];
            //All primitives are allowed as input (Symbol not included). If not a primitive, raise error.
            if (Object.prototype.toString.call(argument) !== "[object Undefined]" && Object.prototype.toString.call(argument) !== "[object Null]" && Object.prototype.toString.call(argument) !== "[object String]" && Object.prototype.toString.call(argument) !== "[object Number]" && Object.prototype.toString.call(argument) !== "[object Boolean]")   
            {
                throw new SyntaxError;
            }
            else
            {
                if (this.contains(argument)) //check if classList contains the argument.
                {
                    this.remove(argument); //if so remove
                }
                else
                {
                    this.add(argument); //if not add
                }                   
            }
        }
    }
    else
    {
        throw new Error("The argument is not optional");
    }
    return undefined; //return undefined as with add and remove.
}       
}

multiToggle does not have the force ability of the original toggle. It just turns class names on and off for as many arguments as supplied.

Warning, expanding fixed Objects can cause troubles in the future . When an object gets deprecated or changed your functionality could break, requiring to more maintenance.

1

There is no direct way but you can create a helper function:

const toggleClass =  (el, cls) => {
    if (Array.isArray(cls)) {
        cls.map((cl) => {
            el.classList.toggle(cl);
        });
    } else {
        el.classList.toggle(cls);
    }
};

Now just call toggleClass() like below:

// single class
toggleClass(document.querySelector('body'), 'left-to-right');
//multiple classes
toggleClass(document.querySelector('body'), ['left-to-right', 'right-to-left']);
1

If I need to toggle multiple classes I just create an array and then iterate through it.

  var classes = [
    "red",
    "blue",
    "green",
    "purple"
  ]

  for (var i = 0; i < classes.length; i++){
    p.classList.toggle(classes[i])
  }

1
  • 1
    This just toggles through everything and leaves it on the last one. I dont think this is what OP wanted. Commented Apr 30, 2020 at 19:55
1

Assuming that myElement is a valid DOM Element, this works:

['right-to-left', 'left-to-right'].forEach(function(className){
  this.classList.toggle(className);
}, myElement);
4
  • 1
    I think you misunderstand the way classList.toggle() works. It does allow for a second argument, but that is used as a conditional for the first argument, which holds the class to toggle. It does not allow for two classes to be toggled at once. See developer.mozilla.org/en-US/docs/Web/API/Element/classList
    – agrm
    Commented May 27, 2020 at 20:51
  • @agrm Thanks for the correction. I will edit my answer shortly so that it doesn't mislead others.
    – rayalois22
    Commented May 27, 2020 at 22:15
  • @rayalois22 Can you please edit the answer? It doesn't work! Commented Aug 2, 2020 at 4:29
  • @safwan-samsudeen, I have edited my answer and the current version works well on my end. I hope someone finds it useful
    – rayalois22
    Commented Oct 19, 2020 at 8:09
1

This Worked for me

let superToggle = (element, class0, class1) => {
 element.classList.toggle(class0);
 element.classList.toggle(class1);
};
1
const toggleClasses = (e, classes) => {
  classes.forEach((className) => {
    e.classList.toggle(className)
  });
}

const classes = [
  'hidden',
  'bg-white',
]

toggleClasses(element, classes)
1
  • 3
    Please read How do I write a good answer?. While this code block may answer the OP's question, this answer would be much more useful if you explain how this code is different from the code in the question, what you've changed, why you've changed it and why that solves the problem without introducing others. Commented Jul 27, 2022 at 3:06
0

The following should work; granted that these class-names are defined in your CSS and some elements on the current page have these classNames:

var toggleDirection = function()
{
    var ltr, rtl, lst, cls;

    ltr = 'left-to-right';
    rtl = 'right-to-left';
    lst = [].slice.call(document.getElementsByClassName(ltr));

    lst = ((lst.length > 0) ? lst : [].slice.call(document.getElementsByClassName(rtl)));

    lst.forEach
    (
        function(node)
        {
            cls = node.getAttribute('class');

            if (cls.indexOf(ltr) > -1)
            { cls.split(ltr).join(rtl); }
            else
            { cls.split(rtl).join(ltr); }

            node.setAttribute('class', cls);
        }
    );
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.