I'm looking for a fast and secure way to add and remove classes from an html element without jQuery.
It also should be working in early IE (IE8 and up).
Another approach to add the class to element using pure JavaScript
For adding class:
document.getElementById("div1").classList.add("classToBeAdded");
For removing class:
document.getElementById("div1").classList.remove("classToBeRemoved");
Note: but not supported in IE <= 9 or Safari <=5.0
The following 3 functions work in browsers which don't support classList:
function hasClass(el, className)
{
if (el.classList)
return el.classList.contains(className);
return !!el.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)'));
}
function addClass(el, className)
{
if (el.classList)
el.classList.add(className)
else if (!hasClass(el, className))
el.className += " " + className;
}
function removeClass(el, className)
{
if (el.classList)
el.classList.remove(className)
else if (hasClass(el, className))
{
var reg = new RegExp('(\\s|^)' + className + '(\\s|$)');
el.className = el.className.replace(reg, ' ');
}
}
https://jaketrent.com/post/addremove-classes-raw-javascript/
For future friendliness, I second the recommendation for classList with polyfill/shim: https://developer.mozilla.org/en-US/docs/Web/API/Element/classList#wrapper
var elem = document.getElementById( 'some-id' );
elem.classList.add('some-class'); // Add class
elem.classList.remove('some-other-class'); // Remove class
elem.classList.toggle('some-other-class'); // Add or remove class
if ( elem.classList.contains('some-third-class') ) { // Check for class
console.log('yep!');
}
classList is available from IE10 onwards, use that if you can.
element.classList.add("something");
element.classList.remove("some-class");
I'm baffled none of the answers here prominently mentions the incredibly useful DOMTokenList.prototype.toggle method, which really simplifies alot of code.
E.g. you often see code that does this:
if (element.classList.contains(className) {
element.classList.remove(className)
} else {
element.classList.add(className)
}
This can be replaced with a simple call to
element.classList.toggle(className)
What is also very helpful in many situations, if you are adding or removing a class name based on a condition, you can pass that condition as a second argument. If that argument is truthy, toggle acts as add, if it's falsy, it acts as though you called remove.
element.classList.toggle(className, condition) // add if condition truthy, otherwise remove
To add class without JQuery just append yourClassName to your element className
document.documentElement.className += " yourClassName";
To remove class you can use replace() function
document.documentElement.className.replace(/(?:^|\s)yourClassName(?!\S)/,'');
Also as #DavidThomas mentioned you'd need to use the new RegExp() constructor if you want to pass class names dynamically to the replace function.
Add & Remove Classes (tested on IE8+)
Add trim() to IE (taken from: .trim() in JavaScript not working in IE)
if(typeof String.prototype.trim !== 'function') {
String.prototype.trim = function() {
return this.replace(/^\s+|\s+$/g, '');
}
}
Add and Remove Classes:
function addClass(element,className) {
var currentClassName = element.getAttribute("class");
if (typeof currentClassName!== "undefined" && currentClassName) {
element.setAttribute("class",currentClassName + " "+ className);
}
else {
element.setAttribute("class",className);
}
}
function removeClass(element,className) {
var currentClassName = element.getAttribute("class");
if (typeof currentClassName!== "undefined" && currentClassName) {
var class2RemoveIndex = currentClassName.indexOf(className);
if (class2RemoveIndex != -1) {
var class2Remove = currentClassName.substr(class2RemoveIndex, className.length);
var updatedClassName = currentClassName.replace(class2Remove,"").trim();
element.setAttribute("class",updatedClassName);
}
}
else {
element.removeAttribute("class");
}
}
Usage:
var targetElement = document.getElementById("myElement");
addClass(targetElement,"someClass");
removeClass(targetElement,"someClass");
A working JSFIDDLE:
http://jsfiddle.net/fixit/bac2vuzh/1/
Try this:
const element = document.querySelector('#elementId');
if (element.classList.contains("classToBeRemoved")) {
element.classList.remove("classToBeRemoved");
}
I'm using this simple code for this task:
CSS Code
.demo {
background: tomato;
color: white;
}
Javascript code
function myFunction() {
/* Assign element to x variable by id */
var x = document.getElementById('para);
if (x.hasAttribute('class') {
x.removeAttribute('class');
} else {
x.setAttribute('class', 'demo');
}
}
Updated JS Class Method
The add methods do not add duplicate classes and the remove method only removes class with exact string match.
const addClass = (selector, classList) => {
const element = document.querySelector(selector);
const classes = classList.split(' ')
classes.forEach((item, id) => {
element.classList.add(item)
})
}
const removeClass = (selector, classList) => {
const element = document.querySelector(selector);
const classes = classList.split(' ')
classes.forEach((item, id) => {
element.classList.remove(item)
})
}
addClass('button.submit', 'text-white color-blue') // add text-white and color-blue classes
removeClass('#home .paragraph', 'text-red bold') // removes text-red and bold classes
You can also do
elem.classList[test ? 'add' : 'remove']('class-to-add-or-remove');
Instead of
if (test) {
elem.classList.add('class-to-add-or-remove');
} else {
elem.classList.remove('class-to-add-or-remove');
}
When you remove RegExp from the equation you leave a less "friendly" code, but it still can be done with the (much) less elegant way of split().
function removeClass(classString, toRemove) {
classes = classString.split(' ');
var out = Array();
for (var i=0; i<classes.length; i++) {
if (classes[i].length == 0) // double spaces can create empty elements
continue;
if (classes[i] == toRemove) // don't include this one
continue;
out.push(classes[i])
}
return out.join(' ');
}
This method is a lot bigger than a simple replace() but at least it can be used on older browsers. And in case the browser doesn't even support the split() command it's relatively easy to add it using prototype.
Just in case someone needs to toggle class on click and remove on other elements in JS only. You can try to do following :
var accordionIcon = document.querySelectorAll('.accordion-toggle');
//add only on first element, that was required in my case
accordionIcon[0].classList.add('close');
for (i = 0; i < accordionIcon.length; i++) {
accordionIcon[i].addEventListener("click", function(event) {
for (i = 0; i < accordionIcon.length; i++) {
if(accordionIcon[i] !== event.target){
accordionIcon[i].classList.remove('close');
}
event.target.classList.toggle("close");
}
})
}
Related
How do you do jQuery’s hasClass with plain ol’ JavaScript? For example,
<body class="foo thatClass bar">
What’s the JavaScript way to ask if <body> has thatClass?
Simply use classList.contains():
if (document.body.classList.contains('thatClass')) {
// do some stuff
}
Other uses of classList:
document.body.classList.add('thisClass');
// $('body').addClass('thisClass');
document.body.classList.remove('thatClass');
// $('body').removeClass('thatClass');
document.body.classList.toggle('anotherClass');
// $('body').toggleClass('anotherClass');
Browser Support:
Chrome 8.0
Firefox 3.6
IE 10
Opera 11.50
Safari 5.1
classList Browser Support
You can check whether element.className matches /\bthatClass\b/.
\b matches a word break.
Or, you can use jQuery's own implementation:
var className = " " + selector + " ";
if ( (" " + element.className + " ").replace(/[\n\t]/g, " ").indexOf(" thatClass ") > -1 )
To answer your more general question, you can look at jQuery's source code on github or at the source for hasClass specifically in this source viewer.
The most effective one liner that
returns a boolean (as opposed to Orbling's answer)
Does not return a false positive when searching for thisClass on an element that has class="thisClass-suffix".
is compatible with every browser down to at least IE6
function hasClass( target, className ) {
return new RegExp('(\\s|^)' + className + '(\\s|$)').test(target.className);
}
// 1. Use if for see that classes:
if (document.querySelector(".section-name").classList.contains("section-filter")) {
alert("Grid section");
// code...
}
<!--2. Add a class in the .html:-->
<div class="section-name section-filter">...</div>
The attribute that stores the classes in use is className.
So you can say:
if (document.body.className.match(/\bmyclass\b/)) {
....
}
If you want a location that shows you how jQuery does everything, I would suggest:
http://code.jquery.com/jquery-1.5.js
Element.matches()
Instead of $(element).hasClass('example') in jQuery, you can use element.matches('.example') in plain JavaScript:
if (element.matches('.example')) {
// Element has example class ...
}
View Browser Compatibility
hasClass function:
HTMLElement.prototype.hasClass = function(cls) {
var i;
var classes = this.className.split(" ");
for(i = 0; i < classes.length; i++) {
if(classes[i] == cls) {
return true;
}
}
return false;
};
addClass function:
HTMLElement.prototype.addClass = function(add) {
if (!this.hasClass(add)){
this.className = (this.className + " " + add).trim();
}
};
removeClass function:
HTMLElement.prototype.removeClass = function(remove) {
var newClassName = "";
var i;
var classes = this.className.replace(/\s{2,}/g, ' ').split(" ");
for(i = 0; i < classes.length; i++) {
if(classes[i] !== remove) {
newClassName += classes[i] + " ";
}
}
this.className = newClassName.trim();
};
I use a simple/minimal solution, one line, cross browser, and works with legacy browsers as well:
/\bmyClass/.test(document.body.className) // notice the \b command for whole word 'myClass'
This method is great because does not require polyfills and if you use them for classList it's much better in terms of performance. At least for me.
Update: I made a tiny polyfill that's an all round solution I use now:
function hasClass(element,testClass){
if ('classList' in element) { return element.classList.contains(testClass);
} else { return new Regexp(testClass).exec(element.className); } // this is better
//} else { return el.className.indexOf(testClass) != -1; } // this is faster but requires indexOf() polyfill
return false;
}
For the other class manipulation, see the complete file here.
a good solution for this is to work with classList and contains.
i did it like this:
... for ( var i = 0; i < container.length; i++ ) {
if ( container[i].classList.contains('half_width') ) { ...
So you need your element and check the list of the classes. If one of the classes is the same as the one you search for it will return true if not it will return false!
This 'hasClass' function works in IE8+, FireFox and Chrome:
hasClass = function(el, cls) {
var regexp = new RegExp('(\\s|^)' + cls + '(\\s|$)'),
target = (typeof el.className === 'undefined') ? window.event.srcElement : el;
return target.className.match(regexp);
}
[Updated Jan'2021] A better way:
hasClass = (el, cls) => {
[...el.classList].includes(cls); //cls without dot
};
Use something like:
Array.prototype.indexOf.call(myHTMLSelector.classList, 'the-class');
if (document.body.className.split(/\s+/).indexOf("thatClass") !== -1) {
// has "thatClass"
}
Well all of the above answers are pretty good but here is a small simple function I whipped up. It works pretty well.
function hasClass(el, cn){
var classes = el.classList;
for(var j = 0; j < classes.length; j++){
if(classes[j] == cn){
return true;
}
}
}
What do you think about this approach?
<body class="thatClass anotherClass"> </body>
var bodyClasses = document.querySelector('body').className;
var myClass = new RegExp("thatClass");
var trueOrFalse = myClass.test( bodyClasses );
https://jsfiddle.net/5sv30bhe/
<div id="test" class="a1 a2 a5"></div>
var element = document.getElementById("test")
if (hasAnyOfTheseClasses(element, ["a1", "a6"])) {
//...
}
Looking for a simple, lightweight function to check if a function has any of the listed classes without jQuery or another library.
Such function would be easy to implement, but there should be a canonical, fastest and simplest answer people can just copy-paste.
This seems vampire-ish, but I'm asking this so googlers won't have to write it themselves.
Not a duplicate - the linked question checks for one class, this question asks for checking any of the classes.
A jQuery version exists here.
Here's a functional implementation using Array.some and Element.classList.contains.
function hasAnyClass(element, classes) {
return classes.some(function(c) {
return element.classList.contains(c);
});
}
var div = document.getElementById("test");
console.log(hasAnyClass(div, ["hi", "xyz"]));
console.log(hasAnyClass(div, ["xyz", "there"]));
console.log(hasAnyClass(div, ["xyz", "xyz"]));
<div id="test" class="hi there"></div>
Note that these functions are not supported on older versions of IE, and will require a shim/polyfill.
You could use a regex, not sure that it's purely better but at least more flexible since your current test relies too much on spaces being entered correctly.
function hasAnyOfTheseClasses(element, classes) {
var className = element.className;
for (var i = 0; i < classes.length; i++) {
var exp = new RegExp('\b'+classes[i] + '\b');
if(exp.test(className)) return true;
}
return false;
}
just create a loop that check if each value in your array is a class in your passed element
function hasAnyOfTheseClasses(elem, tofind) {
classes = elem.className.split(' ');
for(var x in tofind) {
var className = tofind[x];
if (classes.indexOf(className) == -1){
return false;
}
}
return true;
}
Here's my implementation:
function hasAnyOfTheseClasses(element, classes) {
for (var i = 0; i < classes.length; i++) {
if ((' ' + element.className + ' ').indexOf(' ' + classes[i] + ' ') > -1) {
return true;
}
}
return false;
}
It's not elegant or fast. Works though. Feel free to edit to improve.
Use the .classList property to get the list of classes of an element. Then you can use the .contains() method to test each of the classes.
function hasAnyOfTheseClass(element, classes) {
var classList = element.classList;
return classes.some(function(class) {
return classList.contains(class);
});
}
How about using Array.prototype.some() and Array.prototype.indexOf():
function hasAnyClass(el, classes) {
var elClasses = el.className.split(' ');
return classes.some(c => elClasses.indexOf(c) >= 0)
}
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?
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');
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>
just use the map array.
like
['left-to-right', 'right-to-left'].map(v=> group.classList.toggle(v) )
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);
});
};
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)
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.
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']);
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])
}
Assuming that myElement is a valid DOM Element, this works:
['right-to-left', 'left-to-right'].forEach(function(className){
this.classList.toggle(className);
}, myElement);
This Worked for me
let superToggle = (element, class0, class1) => {
element.classList.toggle(class0);
element.classList.toggle(class1);
};
const toggleClasses = (e, classes) => {
classes.forEach((className) => {
e.classList.toggle(className)
});
}
const classes = [
'hidden',
'bg-white',
]
toggleClasses(element, classes)
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);
}
);
}
Hello this code gets tells me if current element has class.
e.srcElement.className.indexOf('thisClass') === 0
How do i also check if element or any of its parents have the class?
Nowadays one can use the widely supported JavaScript closest method.
const parentHasClass = element.closest('.thisClass');
Using the parentNode of an element, it's possible to go through the parents list.
Use the following function:
function elementOrAncestorHasClass(element, className) {
if (!element || element.length === 0) {
return false;
}
var parent = element;
do {
if (parent === document) {
break;
}
if (parent.className.indexOf(className) >= 0) {
return true;
}
} while (parent = parent.parentNode);
return false;
}
This fiddle shows the function in action.
You just need to traverse the DOM. To go one parent up, use parentNode.
Do traverse infinitely upwards, you'll need to use a loop:
for(var i = 0; i < nodes.length; i++) {
// check if each parentNode has the class
}
It's a tedious process. That's why libraries like jQuery exist for the sole purpose of DOM traversal/manipulation.
I can offer to use next methods:
function hasClass(element, className) {
return !(!className || !element || !element.className
|| !element.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)')));
}
function parentByClass(childElement, className) {
if (!childElement || childElement === document) {
return null;
} else if (hasClass(childElement, className)) {
return childElement;
} else {
return parentByClass(childElement.parentNode, className)
}
}
function hasClassInTree(element, className) {
return hasClass(element, className) || parentByClass(element, className)
}
So in you case condition will be
if (hasClassInTree(e.srcElement, 'thisClass')) { ... }
May be its easier to find all the elements that have the class, and check if any of them contain the current node (not tested code):
function checkParentClass(e,className){
var nodesWithClass =document.getElementsByClassName("className");
var index =0;
var found = false;
while(index< nodesWithClass.length && !found){
if (nodesWithClass[index].contains(e))
{
found = true;
}
index++;
}
return found;
}
I'm building a JavaScript plugin which will bolt onto other websites.
The whole thing is written with pure JS but there's one bit I haven't been able to get away from jQuery with:
var key = "some_key";
var selector = "#my_input, input[name=my_input], .someInputs";
jQuery(document).on('change', selector, function() {
doSomething(key, this.value);
});
The reason I want to avoid jQuery is that I expect this JS to be included in a wide range of sites, many of which won't have jQuery. Some will have other frameworks already installed such as Mootools, some will have old versions of jQuery where .on() isn't supported, etc.
That, and I am ideally trying to keep it very lightweight, so adding in jQuery just for this tiny task seems excessive.
Here’s some futuristic JavaScript that does exactly the same thing:
var key = "some_key";
var selector = "#my_input, input[name=my_input], .someInputs";
document.addEventListener('change', function (e) {
if (e.target.matches(selector)) {
doSomething(key, e.target.value);
}
});
However, several browsers only support it with a prefix, so it’ll be closer to this:
var matches = (function () {
var names = ['matches', 'matchesSelector', 'mozMatchesSelector', 'webkitMatchesSelector', 'msMatchesSelector'];
for (var i = 0; i < names.length; i++) {
var name = names[i];
if (name in HTMLElement.prototype) {
return HTMLElement.prototype[name];
}
}
return null;
})();
var key = "some_key";
var selector = "#my_input, input[name=my_input], .someInputs";
document.addEventListener('change', function (e) {
if (matches.call(e.target, selector)) {
doSomething(key, e.target.value);
}
});
Assuming the selector isn’t dynamic and you need delegation, you can still do the verbose, manual check:
var key = "some_key";
document.addEventListener('change', function (e) {
var target = e.target;
if (target.id === 'my_input' ||
target.nodeName === 'INPUT' && target.name === 'my_input' ||
(' ' + target.className + ' ').indexOf(' someInputs ') !== -1) {
doSomething(key, target.value);
}
}, false);
As #T.J. Crowder points out, although this works for input elements, you’ll need to check an element’s parents in many cases. Here’s some even more futuristic JavaScript to accomplish the task:
function* ascend(element) {
do {
yield element;
} while ((element = element.parentNode));
}
var key = "some_key";
var selector = "#my_input, input[name=my_input], .someInputs";
document.addEventListener('change', function (e) {
var match = Array.from(ascend(e.target)).find(x => x.matches(selector));
if (match) {
doSomething(key, match.value);
}
});
If you smashed Firefox Nightly and Chrome together, this would work in that browser. We don’t have that, but feel free to shim Array.prototype.find!