Code mit classList funktioniert nicht im IE?

83

Ich verwende den folgenden Code, aber er schlägt im IE fehl. Die Nachricht lautet:

Wert der Eigenschaft 'add' kann nicht abgerufen werden: Objekt ist null oder undefiniert "

Ich gehe davon aus, dass dies nur ein IE-Support-Problem ist. Wie würden Sie den folgenden Code in IE funktionieren lassen?

Irgendwelche Ideen?

var img = new Image();
img.src = '/image/file.png';
img.title = 'this is a title';
img.classList.add("profilePic");
var div = document.createElement("div");
div.classList.add("picWindow");
div.appendChild(img);
content.appendChild(div);
Wesley
quelle
5
IE hat keine Klassenliste, daher ist sie null oder undefiniert
Esailija
2
Möchten Sie jQuery verwenden oder nicht?
Arb
1
@ Zero21xxx Das macht mir nichts aus, aber dafür müsste ich den gesamten obigen Code neu schreiben, um JQuery-Objekte anstelle von Standard-JS zu verwenden.
Wesley
3
Ich habe hierclassList einige Polyfills für geschrieben .
alex
1
@alex Dein Blog-Beitrag könnte ein Update vertragen. Erstens ist der letzte Link (MDN) falsch, querySelectorsollte sein classList. Zweitens .removeenthält die Methode ein unnötiges RegExp, dessen Verwendung - wie Sie bestätigt haben - einen Fehler verursacht. Da Sie bereits Leerzeichen vor und nach dem Suffix haben, ist ein einfaches .replace(' ' + className + ' ', ' ')ausreichend (außerdem ist die Bemerkung "Ein gültiger Klassenname sollte keine regulären Regex-Zeichen enthalten." Falsch, siehe Spezifikation (gilt auch für HTML4))
Rob W

Antworten:

92

Die classListEigenschaft wird von IE9 und niedriger nicht unterstützt. IE10 + unterstützt es jedoch.
Verwenden Sie className += " .."stattdessen. Hinweis: Lassen Sie das Leerzeichen nicht aus: Klassennamen sollten in eine durch Leerzeichen getrennte Liste eingefügt werden.

var img = new Image();
img.src = '/image/file.png';
img.title = 'this is a title';
img.className += " profilePic"; // Add profilePic class to the image

var div = document.createElement("div");
div.className += " picWindow";  // Add picWindow class to the div
div.appendChild(img);
content.appendChild(div);
Rob W.
quelle
3
@MikeChristensen Das classNameAttribut ist immer vorhanden. Es wird an einer leeren Zeichenfolge initialisiert. Zur einfacheren Pflege des Codes +=wird empfohlen, Klassennamen hinzuzufügen.
Rob W
2
Im strengen Modus wird ein Fehler generiert, da className schreibgeschützt ist.
Jason Aller
1
@JasonAller Das ist höchst unwahrscheinlich. Die classNameEigenschaft eines DOM-Elements ist Lese- / Schreibzugriff.
Rob W
44
IE11 ist immer noch ein Fehler in classList. ClassList in SVG-Elementen wird nicht unterstützt.
Codenamezero
1
und jQuery kann SVG-Elementen auch keine Klassen hinzufügen, sodass die Lösung von oben nur möglich ist, wenn Sie SVG bearbeiten möchten.
Senthe
26

Wie von othes erwähnt, classListwird es von IE9 und älter nicht unterstützt. Neben der obigen Alternative von Alex gibt es einige Polyfills, die als Ersatz dienen sollen, dh nur diese in Ihre Seite aufnehmen und der IE sollte einfach funktionieren (berühmte letzte Wörter!).

https://github.com/eligrey/classList.js/blob/master/classList.js

https://gist.github.com/devongovett/1381839

Tagawa
quelle
11
Ich benutze IE 11 und classList scheint nicht zu funktionieren (funktioniert in Chrom)
John Ktejik
1
Das ist seltsam - caniuse.com sagt, dass es von IE10 + unterstützt werden sollte. Ich schlage vor, @TheWebJustWorks auf Twitter zu fragen oder ein Problem bei webcompat.com einzureichen
tagawa
3
@johnktejik Wenn Sie mehrere Klassen gleichzeitig hinzufügen oder entfernen, funktioniert dies in IE11, Firefox 24- und Chrome 24- nicht. War es das, was du getan hast?
mnsth
1
@BradAdams Hier und hier . Caniuse und MDN haben auch Berichte. Alle diese Probleme wurden jedoch in Microsoft Edge behoben!
mnsth
2
classList wird immer noch nicht von SVG-Elementen unterstützt, diese Polyfills machen den Trick
Shoplifter.Doe
10
    /**
 * Method that checks whether cls is present in element object.
 * @param  {Object} ele DOM element which needs to be checked
 * @param  {Object} cls Classname is tested
 * @return {Boolean} True if cls is present, false otherwise.
 */
function hasClass(ele, cls) {
    return ele.getAttribute('class').indexOf(cls) > -1;
}

/**
 * Method that adds a class to given element.
 * @param  {Object} ele DOM element where class needs to be added
 * @param  {Object} cls Classname which is to be added
 * @return {null} nothing is returned.
 */
function addClass(ele, cls) {
    if (ele.classList) {
        ele.classList.add(cls);
    } else if (!hasClass(ele, cls)) {
        ele.setAttribute('class', ele.getAttribute('class') + ' ' + cls);
    }
}

/**
 * Method that does a check to ensure that class is removed from element.
 * @param  {Object} ele DOM element where class needs to be removed
 * @param  {Object} cls Classname which is to be removed
 * @return {null} Null nothing is returned.
 */
function removeClass(ele, cls) {
    if (ele.classList) {
        ele.classList.remove(cls);
    } else if (hasClass(ele, cls)) {
        ele.setAttribute('class', ele.getAttribute('class').replace(cls, ' '));
    }
}
Ritesh Kumar
quelle
1
+1 jedoch: Ich hatte ele.getAttribute('class')in einigen Fällen die Rückgabe null (möglicherweise, wenn das Klassenattribut noch nicht festgelegt ist?) - eine einfache if-Anweisung löste es.
Jeppe
8

Schau dir das an

Object.defineProperty(Element.prototype, 'classList', {
    get: function() {
        var self = this, bValue = self.className.split(" ")

        bValue.add = function (){
            var b;
            for(i in arguments){
                b = true;
                for (var j = 0; j<bValue.length;j++)
                    if (bValue[j] == arguments[i]){
                        b = false
                        break
                    }
                if(b)
                    self.className += (self.className?" ":"")+arguments[i]
            }
        }
        bValue.remove = function(){
            self.className = ""
            for(i in arguments)
                for (var j = 0; j<bValue.length;j++)
                    if(bValue[j] != arguments[i])
                        self.className += (self.className?" " :"")+bValue[j]
        }
        bValue.toggle = function(x){
            var b;
            if(x){
                self.className = ""
                b = false;
                for (var j = 0; j<bValue.length;j++)
                    if(bValue[j] != x){
                        self.className += (self.className?" " :"")+bValue[j]
                        b = false
                    } else b = true
                if(!b)
                    self.className += (self.className?" ":"")+x
            } else throw new TypeError("Failed to execute 'toggle': 1 argument required")
            return !b;
        }

        return bValue; 
    },
    enumerable: false
})

und classList wird funktionieren!

document.getElementsByTagName("div")[0].classList
["aclass"]

document.getElementsByTagName("div")[0].classList.add("myclass")

document.getElementsByTagName("div")[0].className
"aclass myclass"

das ist alles!

asdru
quelle
Ich bin ein Noob. Wo platziere ich das oben genannte? Im <script> oder im <style>?
Fandango68
Ich habe diesen Code in einen Skriptblock direkt über dem fehlerhaften Code eingefügt - funktioniert sehr gut. Vielen Dank.
Robnick
1
Gute Arbeit, leider funktioniert dies nicht in allen Fällen für mich, ich habe diese jedoch gefunden: github.com/remy/polyfills/blob/…
Michiel
7

In IE 10 und 11 ist die classList-Eigenschaft in HTMLElement.prototype definiert.

Damit es auf SVG-Elementen funktioniert, sollte die Eigenschaft in Element.prototype definiert werden, wie es in anderen Browsern der Fall war.

Ein sehr kleiner Fix wäre das Kopieren des genauen propertyDescriptor von HTMLElement.prototype nach Element.prototype:

if (!Object.getOwnPropertyDescriptor(Element.prototype,'classList')){
    if (HTMLElement&&Object.getOwnPropertyDescriptor(HTMLElement.prototype,'classList')){
        Object.defineProperty(Element.prototype,'classList',Object.getOwnPropertyDescriptor(HTMLElement.prototype,'classList'));
    }
}
  • Wir müssen den Deskriptor kopieren, da Element.prototype.classList = HTMLElement.prototype.classList wird werfenInvalid calling object
  • Die erste Überprüfung verhindert das Überschreiben der Eigenschaft in Browsern, die von Haus aus unterstützt werden.
  • Die zweite Überprüfung verhindert Fehler in IE-Versionen vor 9, in denen HTMLElement noch nicht implementiert ist, und in IE9, in denen classList nicht implementiert ist.

Verwenden Sie für IE 8 und 9 den folgenden Code. Ich habe auch eine (minimierte) Polyfüllung für Array.prototype.indexOf eingefügt, da IE 8 diese nicht nativ unterstützt (Polyfill-Quelle: Array.prototype.indexOf)

//Polyfill Array.prototype.indexOf
Array.prototype.indexOf||(Array.prototype.indexOf=function (value,startIndex){'use strict';if (this==null)throw new TypeError('array.prototype.indexOf called on null or undefined');var o=Object(this),l=o.length>>>0;if(l===0)return -1;var n=startIndex|0,k=Math.max(n>=0?n:l-Math.abs(n),0)-1;function sameOrNaN(a,b){return a===b||(typeof a==='number'&&typeof b==='number'&&isNaN(a)&&isNaN(b))}while(++k<l)if(k in o&&sameOrNaN(o[k],value))return k;return -1});

// adds classList support (as Array) to Element.prototype for IE8-9
Object.defineProperty(Element.prototype,'classList',{
    get:function(){
        var element=this,domTokenList=(element.getAttribute('class')||'').replace(/^\s+|\s$/g,'').split(/\s+/g);
        if (domTokenList[0]==='') domTokenList.splice(0,1);
        function setClass(){
            if (domTokenList.length > 0) element.setAttribute('class', domTokenList.join(' ');
            else element.removeAttribute('class');
        }
        domTokenList.toggle=function(className,force){
            if (force!==undefined){
                if (force) domTokenList.add(className);
                else domTokenList.remove(className);
            }
            else {
                if (domTokenList.indexOf(className)!==-1) domTokenList.splice(domTokenList.indexOf(className),1);
                else domTokenList.push(className);
            }
            setClass();
        };
        domTokenList.add=function(){
            var args=[].slice.call(arguments)
            for (var i=0,l=args.length;i<l;i++){
                if (domTokenList.indexOf(args[i])===-1) domTokenList.push(args[i])
            };
            setClass();
        };
        domTokenList.remove=function(){
            var args=[].slice.call(arguments)
            for (var i=0,l=args.length;i<l;i++){
                if (domTokenList.indexOf(args[i])!==-1) domTokenList.splice(domTokenList.indexOf(args[i]),1);
            };
            setClass();
        };
        domTokenList.item=function(i){
            return domTokenList[i];
        };
        domTokenList.contains=function(className){
            return domTokenList.indexOf(className)!==-1;
        };
        domTokenList.replace=function(oldClass,newClass){
            if (domTokenList.indexOf(oldClass)!==-1) domTokenList.splice(domTokenList.indexOf(oldClass),1,newClass);
            setClass();
        };
        domTokenList.value = (element.getAttribute('class')||'');
        return domTokenList;
    }
});
Kevin Drost
quelle
Der erste Code schnippte gut, aber es fehlten zwei schließende Klammern ).
Superdweebie
@superdweebie, danke fürs bemerken. Ich habe meine Antwort korrigiert
Kevin Drost
3

In Explorer 11 funktioniert classList.add nur mit einzelnen Werten.

Element.classList.add("classOne", "classTwo");

In diesem Fall fügt der Explorer nur die erste Klasse hinzu und ignoriert die zweite. Also muss man tun:

Element.classList.add("classOne");
Element.classList.add("classTwo");
Alonad
quelle
0

classListwird in IE <9 nicht unterstützt. Verwenden Sie jQuery.addClass oder eine Polyfüllung wie die unter https://developer.mozilla.org/en-US/docs/Web/API/Element/classList

Juan Mendes
quelle
1
Für jeden, der dies abgelehnt hat: Dies war wahr, als die Antwort veröffentlicht wurde, und nur IE 9 begann, sie zu unterstützen
Juan Mendes
2
Ich würde eine Vermutung wagen, dass es abgelehnt wurde, weil die Frage nicht nach jQuery fragte. Es gibt Möglichkeiten, Probleme zu umgehen, bei denen nicht die gesamte Javascript-Bibliothek verwendet wird.
Vincent McNabb
4
-1: ja .. richtig ... lassen Sie uns 100KB Bibliothek hinzufügen, um die classListFunktionalität schlecht nachzuahmen
tereško
1
@ tereško Es heißt "oder so ähnlich", falls Sie jQuery noch nicht verwenden. Beachten Sie, dass die akzeptierte Antwort denselben Klassennamen zweimal hinzufügen kann, da nicht geprüft wird, ob er bereits vorhanden ist.
Juan Mendes