The Element.classList is a read-only property which returns a live DOMTokenList collection of the class attributes of the element.

Using classList is a convenient alternative to accessing an element's list of classes as a space-delimited string via element.className.


const elementClasses = elementNodeReference.classList;

elementClasses is a DOMTokenList representing the class attribute of elementNodeReference. If the class attribute was not set or is empty elementClasses.length returns 0. element.classList itself is read-only, although you can modify it using the add() and remove() methods.


add( String [, String [, ...]] )
Add specified class values. If these classes already exist in attribute of the element, then they are ignored.
remove( String [, String [, ...]] )
Remove specified class values.
Note: Removing a class that does not exist does NOT throw an error.
item( Number )
Return class value by index in collection.
toggle( String [, force] )
When only one argument is present: Toggle class value; i.e., if class exists then remove it and return false, if not, then add it and return true.
When a second argument is present: If the second argument evaluates to true, add specified class value, and if it evaluates to false, remove it.
contains( String )
Checks if specified class value exists in class attribute of the element.
replace( oldClass, newClass )
Replaces an existing class with a new class.


const div = document.createElement('div');
div.className = 'foo';

// our starting state: <div class="foo"></div>

// use the classList API to remove and add classes

// <div class="anotherclass"></div>

// if visible is set remove it, otherwise add it

// add/remove visible, depending on test conditional, i less than 10
div.classList.toggle("visible", i < 10 );


// add or remove multiple classes
div.classList.add("foo", "bar", "baz");
div.classList.remove("foo", "bar", "baz");

// add or remove multiple classes using spread syntax
const cls = ["foo", "bar"];

// replace class "foo" with class "bar"
div.classList.replace("foo", "bar");

Versions of Firefox before 26 do not implement the use of several arguments in the add/remove/toggle methods. See


The legacy onpropertychange event can be used to create a live classList mockup on account of the fact that there exists a Element.prototype.className property that will fire this event when changed. The below polyfill will polyfill both classList and its corrosponding DOMTokenList. The below polyfill ensures full standards compliance for all DOMTokenList methods and accessing Element.prototype.classList in IE10-IE11 plus "standard-enough" (for most general application) behavior in IE6-9.

// 1. String.prototype.trim polyfill
if (!"".trim) String.prototype.trim = function(){ return this.replace(/^[s]+|[s]+$/g, ''); };
(function(window){"use strict"; // prevent global namespace pollution
function checkIfValidClassListEntry(O, V){
if (V === "") throw new DOMException(
  "Failed to execute '" + O + "' on 'DOMTokenList': The token provided must not be empty." );
if((!==-1)throw new DOMException("Failed to execute '"+O+"' on 'DOMTokenList': "+
"The token provided ('"+V[wsI]+"') contains HTML space characters, which are not valid in tokens.");
// 2. Implement the barebones DOMTokenList livelyness polyfill
if (typeof DOMTokenList !== "function") (function(window){
    var document = window.document, Object = window.Object, hasOwnProp = Object.prototype.hasOwnProperty;
    var defineProperty = Object.defineProperty, allowTokenListConstruction = 0, skipPropChange = 0;
    var Element = window.Element, wsI = 0, wsRE = /[\11\12\14\15\40]/; // WhiteSpace Regular Expression
    function DOMTokenList(){
        if (!allowTokenListConstruction) throw TypeError("Illegal constructor"); // internally let it through
    DOMTokenList.prototype.toString = DOMTokenList.prototype.toLocaleString = function(){return this.value};
    DOMTokenList.prototype.add = function(){
        a: for(var v=0, argLen=arguments.length,val="",ele=this["uCL"],proto=ele[" uCLp"]; v!==argLen; ++v) {
            val = arguments[v] + "", checkIfValidClassListEntry("add", val);
            for (var i=0, Len=proto.length, resStr=val; i !== Len; ++i)
                if (this[i] === val) continue a; else resStr += " " + this[i];
            this[Len] = val, proto.length += 1, proto.value = resStr;
        skipPropChange = 1, ele.className = proto.value, skipPropChange = 0;
    DOMTokenList.prototype.remove = function(){
        for (var v=0, argLen=arguments.length,val="",ele=this["uCL"],proto=ele[" uCLp"]; v !== argLen; ++v) {
            val = arguments[v] + "", checkIfValidClassListEntry("remove", val);
            for (var i=0, Len=proto.length, resStr="", is=0; i !== Len; ++i)
                if(is){ this[i-1]=this[i] }else{ if(this[i] !== val){ resStr+=this[i]+" "; }else{ is=1; } }
            if (!is) continue;
            delete this[Len], proto.length -= 1, proto.value = resStr;
        skipPropChange = 1, ele.className = proto.value, skipPropChange = 0;
    window.DOMTokenList = DOMTokenList;
    function whenPropChanges(){
        var evt = window.event, prop = evt.propertyName;
        if ( !skipPropChange && (prop==="className" || (prop==="classList" && !defineProperty)) ) {
            var target = evt.srcElement, protoObjProto = target[" uCLp"], strval = "" + target[prop];
            var tokens=strval.trim().split(wsRE), resTokenList=target[prop==="classList"?" uCL":"classList"];
            var oldLen = protoObjProto.length;
            a: for(var cI = 0, cLen = protoObjProto.length = tokens.length, sub = 0; cI !== cLen; ++cI){
                for(var innerI=0; innerI!==cI; ++innerI) if(tokens[innerI]===tokens[cI]) {sub++; continue a;}
                resTokenList[cI-sub] = tokens[cI];
            for (var i=cLen-sub; i < oldLen; ++i) delete resTokenList[i]; //remove trailing indexs
            if(prop !== "classList") return;
            skipPropChange = 1, target.classList = resTokenList, target.className = strval;
            skipPropChange = 0, resTokenList.length = tokens.length - sub;
    function polyfillClassList(ele){
        if (!ele || !("innerHTML" in ele)) throw TypeError("Illegal invocation");
        srcEle.detachEvent( "onpropertychange", whenPropChanges ); // prevent duplicate handler infinite loop
        allowTokenListConstruction = 1;
        try{ function protoObj(){} protoObj.prototype = new DOMTokenList(); }
        finally { allowTokenListConstruction = 0 }
        var protoObjProto = protoObj.prototype, resTokenList = new protoObj();
        a: for(var toks=ele.className.trim().split(wsRE), cI=0, cLen=toks.length, sub=0; cI !== cLen; ++cI){
            for (var innerI=0; innerI !== cI; ++innerI) if (toks[innerI] === toks[cI]) { sub++; continue a; }
            this[cI-sub] = toks[cI];
        protoObjProto.length = Len-sub, protoObjProto.value = ele.className, protoObjProto[" uCL"] = ele;
        if (defineProperty) { defineProperty(ele, "classList", { // IE8 & IE9 allow defineProperty on the DOM
            enumerable:   1, get: function(){return resTokenList},
            configurable: 0, set: function(newVal){
                skipPropChange = 1, ele.className = protoObjProto.value = (newVal += ""), skipPropChange = 0;
                var toks = newVal.trim().split(wsRE), oldLen = protoObjProto.length;
                a: for(var cI = 0, cLen = protoObjProto.length = toks.length, sub = 0; cI !== cLen; ++cI){
                    for(var innerI=0; innerI!==cI; ++innerI) if(toks[innerI]===toks[cI]) {sub++; continue a;}
                    resTokenList[cI-sub] = toks[cI];
                for (var i=cLen-sub; i < oldLen; ++i) delete resTokenList[i]; //remove trailing indexs
        }); defineProperty(ele, " uCLp", { // for accessing the hidden prototype
            enumerable: 0, configurable: 0, writeable: 0, value: protoObj.prototype
        }); defineProperty(protoObjProto, " uCL", {
            enumerable: 0, configurable: 0, writeable: 0, value: ele
        }); } else { ele.classList=resTokenList, ele[" uCL"]=resTokenList, ele[" uCLp"]=protoObj.prototype; }
        srcEle.attachEvent( "onpropertychange", whenPropChanges );
    try { // Much faster & cleaner version for IE8 & IE9:
        // Should work in IE8 because Element.prototype instanceof Node is true according to the specs
        window.Object.defineProperty(window.Element.prototype, "classList", {
            enumerable: 1,   get: function(val){
                                 if (!, "classList")) polyfillClassList(this);
                                 return this.classList;
            configurable: 0, set: function(val){this.className = val}
    } catch(e) { // Less performant fallback for older browsers (IE 6-8):
        window[" uCL"] = polyfillClassList;
        // the below code ensures polyfillClassList is applied to all current and future elements in the doc.
            '_*{x-uCLp:expression(!this.hasOwnProperty("classList")&&window[" uCL"](this))}' + //  IE6
            '[class]{x-uCLp/**/:expression(!this.hasOwnProperty("classList")&&window[" uCL"](this))}' //IE7-8
// 3. Patch in unsupported methods in DOMTokenList
(function(DOMTokenListProto, testClass){
    if (!DOMTokenListProto.item) DOMTokenListProto.item = function(i){
        function NullCheck(n) {return n===void 0 ? null : n} return NullCheck(this[i]);
    if (!DOMTokenListProto.toggle || testClass.toggle("a",0)!==false) DOMTokenListProto.toggle=function(val){
        if (arguments.length > 1) return (this[arguments[1] ? "add" : "remove"](val), !!arguments[1]);
        var oldValue = this.value;
        return (this.remove(oldToken), oldValue === this.value && (this.add(val), true) /*|| false*/);
    if (!DOMTokenListProto.replace || typeof testClass.replace("a", "b") !== "boolean")
        DOMTokenListProto.replace = function(oldToken, newToken){
            checkIfValidClassListEntry("replace", oldToken), checkIfValidClassListEntry("replace", newToken);
            var oldValue = this.value;
            return (this.remove(oldToken), this.value !== oldValue && (this.add(newToken), true));
    if (!DOMTokenListProto.contains) DOMTokenListProto.contains = function(value){
        for (var i=0,Len=this.length; i !== Len; ++i) if (this[i] === value) return true;
        return false;
    if (!DOMTokenListProto.forEach) DOMTokenListProto.forEach = function(f){
        if (arguments.length === 1) for (var i = 0, Len = this.length; i !== Len; ++i) f( this[i], i, this);
        else for (var i=0,Len=this.length,tArg=arguments[1]; i !== Len; ++i), this[i], i, this);
    if (!DOMTokenListProto.entries) DOMTokenListProto.entries = function(){
        var nextIndex = 0, that = this;
        return {next: function() {
            return nextIndex<that.length ? {value: [nextIndex, that[nextIndex]], done: false} : {done: true};
    if (!DOMTokenListProto.values) DOMTokenListProto.values = function(){
        var nextIndex = 0, that = this;
        return {next: function() {
            return nextIndex<that.length ? {value: that[nextIndex], done: false} : {done: true};
    if (!DOMTokenListProto.keys) DOMTokenListProto.keys = function(){
        var nextIndex = 0, that = this;
        return {next: function() {
            return nextIndex<that.length ? {value: nextIndex, done: false} : {done: true};
})(window.DOMTokenList.prototype, window.document.createElement("div").classList);

Please note that the above polyfill is limited in its functionalities. It is currently unable to polyfill out-of-document-elements (e.g. elements created by document.createElement before they are appendChild'ed to a parent node) in IE 6-7. However, it should work just fine in IE9. A major descrepency between the polyfilled version of classList and the W3 specs is in IE6-8, there is no way to create an immutable object (an object whose properties cannot be directly modified). In IE9, however, it is possible through extending the prototye, freezing the visible object, and overwritting native property methods. However, such actions would not work in IE6-IE8 and, in IE9, slow the performance of the entire webpage to a snails crawl, making these modifications completely impractical for this polyfill. A minor note is that in IE6-7, this polyfill uses the window[" uCL"] property on the window object for communicating with the CSS expressions, the x-uCLp css property on all elements, and the element[" uCL"] property on all elements to allow garbadge collection and boost performance. In all polyfilled browsers (IE6-9), an additional element[" uCLp"] property is added to element to ensure standards compliant prototyping, and a DOMTokenList[" uCL"] property is added to each element["classList"] object to ensure that the DOMTokenList is bounded to its own element.


Specification Status Comment
HTML Living Standard
The definition of 'Element.classList' in that specification.
Living Standard Note within the HTML specification related to the class attribute.
The definition of 'Element.classList' in that specification.
Living Standard Initial definition
The definition of 'Element.classList' in that specification.

Browser compatibility

We're converting our compatibility data into a machine-readable JSON format. This compatibility table still uses the old format, because we haven't yet converted the data it contains. Find out how you can help!

Feature Chrome Edge Firefox (Gecko) Internet Explorer Opera Safari (WebKit)
Basic support 8 12 3.6 (1.9.2) 10[1] 11.50 5.1
toggle() method's second argument 24 12 24 (24) No support[2] 15 7
Multiple arguments for add() & remove() 24 12 26 (26) No support 15 7
replace() 61 ? 49 (49) No support No support No support
Feature Android Edge Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile
Basic support 3.0 12 1.0 (1.9.2) 10[1] 11.10 5.0
toggle method's second argument 4.4 12 24.0 (24) No support[2] ? 7.0
multiple arguments for add() & remove() 4.4 12 ? No support ? 7.0
replace() No support No support 49 (49) No support No support No support

[1] Not supported for SVG elements.  See a report at Microsoft about that. This is fixed in Edge 16.16299
[2] Internet Explorer never implemented this. See a report at Microsoft about that.

See also