Element.classList

La proprietà Element.classList di sola lettura restituisce una raccolta DOMTokenList dinamica delle classi dell'elemento.

L'utilizzo di classList è una comoda alternativa all'accesso all'elenco di classi di un elemento come stringa delimitata dallo spazio tramite element.className.

Sintassi

const elementClasses = elementNodeReference.classList;

elementClasses è una DOMTokenList che rappresenta l'attributo class di elementNodeReference. Se l'attributo class non è stato impostato o è vuoto elementClasses.length ritorna 0. element.classList è di sola lettura, sebbene sia possibile modificarlo utilizzando i metodi add()remove().

Metodi

add( String [, String [, ...]] )
Aggiunge le classi specificate. Se queste classi esistono già nell'attributo class dell'elemento, vengono ignorate.
remove( String [, String [, ...]] )

Rimuove le classi specificate.

Nota: La rimozione di una classe inesistente NON genera un errore.
item( Number )
Restituisce il valore della classe per indice nella collezione.
toggle( String [, force] )
Quando è presente un solo argomento: aggiunge/rimuove il valore della classe; ad esempio, se la classe esiste, la rimuove e restituisce false, altrimenti, la aggiunge e restituisce true.

Quando è presente un secondo argomento: Se il secondo argomento restituisce true, aggiunge la classe specificata; se restituisce false, la rimuove.
contains( String )
Verifica se il valore di classe specificato esiste nell'attributo class dell'elemento.
replace( vecchiaClasse, nuovaClasse )
Sostituisce una classe esistente con una nuova classe.

Esempi

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

// il nostro stato iniziale: <div class="foo"></div>
console.log(div.outerHTML);

// usare l'API classList per rimuovere e aggiungere classi
div.classList.remove("foo");
div.classList.add("anotherclass");

// <div class="anotherclass"></div>
console.log(div.outerHTML);

// se visible è impostato rimuovilo, altrimenti aggiungilo
div.classList.toggle("visible");

// aggiungi/rimuovi visible, a seconda del test condizionale, i meno di 10
div.classList.toggle("visible", i < 10 );

console.log(div.classList.contains("foo"));

// aggiungere o rimuovere più classi
div.classList.add("foo", "bar", "baz");
div.classList.remove("foo", "bar", "baz");

// aggiungere o rimuovere più classi utilizzando la spread syntax
const cls = ["foo", "bar"];
div.classList.add(...cls);
div.classList.remove(...cls);

// sostituire la classe "foo" con la classe "bar"
div.classList.replace("foo", "bar");

Le versioni di Firefox precedenti alla 26 non implementano l'uso di diversi argomenti nei metodi add/remove/toggle. Vedi https://bugzilla.mozilla.org/show_bug.cgi?id=814014

Polyfill

L'evento legacy onpropertychange può essere utilizzato per creare un mockup dinamico di classList dinamico grazie alla proprietà Element.prototype.className che attiva l'evento specificato una volta modificato.

Il seguente polyfill sia per classList che per DOMTokenList garantisce la piena conformità (copertura) per tutti i metodi e le proprietà standard di Element.prototype.classList per i browser IE10-IE11 oltre ad un comportamento quasi conforme per IE 6-9. Controlla:

// 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
if(!window.DOMException) (DOMException = function(reason){this.message = reason}).prototype = new Error;
var wsRE = /[\11\12\14\15\40]/, wsIndex = 0, checkIfValidClassListEntry = function(O, V) {
  if (V === "") throw new DOMException(
    "Failed to execute '" + O + "' on 'DOMTokenList': The token provided must not be empty." );
  if((wsIndex=V.search(wsRE))!==-1) throw new DOMException("Failed to execute '"+O+"' on 'DOMTokenList': " +
    "The token provided ('"+V[wsIndex]+"') 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;
    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");
        ele.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 = cLen-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; }
        ele.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 (!hasOwnProp.call(this, "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.
        document.documentElement.firstChild.appendChild(document.createElement('style')).styleSheet.cssText=(
            '_*{x-uCLp:expression(!this.hasOwnProperty("classList")&&window[" uCL"](this))}' + //  IE6
            '[class]{x-uCLp/**/:expression(!this.hasOwnProperty("classList")&&window[" uCL"](this))}' //IE7-8
        );
    }
})(window);
// 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(oldValue), 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) f.call(tArg, 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);
})(window);

Avvertenze

Il polyfill ha funzionalità limitate. Al momento non è in grado di eseguire il polyfill fuori dagli elementi del documento (ad esempio elementi creati da document.createElement prima di essere appesi su un nodo padre) in IE6-7.

Tuttavia, dovrebbe funzionare bene in IE9. Una discrepanza maggiore tra la versione polyfilled di classList e le specifiche W3 è che per IE6-8, non esiste un modo per creare un oggetto immutabile (un oggetto le cui proprietà non possono essere modificate direttamente). In IE9, tuttavia, è possibile estendere il prototipo, congelando l'oggetto visibile e sovrascrivendo i metodi di proprietà native. Tuttavia, tali azioni non funzionerebbero in IE6-IE8 e, in IE9, rallenterebbero le prestazioni dell'intera pagina Web fino alla scansione di una lumaca, rendendo queste modifiche completamente impraticabili per questo polyfill.

Una nota minore è che in IE6-7, questo polyfill usa la proprietà window[" uCL"] sull'oggetto window per comunicare con le espressioni CSS, la proprietà css x-uCLp su tutti gli elementi e la proprietà element[" uCL"] su tutti gli elementi per consentire la raccolta di garbadge e migliorare le prestazioni. In tutti i browser polyfilled (IE6-9), una proprietà aggiuntiva element[" uCLp"] viene aggiunta all'elemento per garantire la prototipazione conforme agli standard e una proprietà DOMTokenList[" uCL"] viene aggiunta ad ogni oggetto element["classList"] per garantire che la DOMTokenList sia limitata al proprio elemento.

Specifiche

Compatibilità con i browser

BCD tables only load in the browser

Vedi anche