Kod z classList nie działa w IE?

83

Używam następującego kodu, ale nie działa w IE. Wiadomość brzmi:

Nie można pobrać wartości właściwości „add”: obiekt jest pusty lub niezdefiniowany ”

Zakładam, że to tylko problem z obsługą IE. Jak sprawić, by następujący kod działał w IE?

Jakieś pomysły?

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
źródło
5
IE nie ma klasy classList, dlatego ma wartość null lub undefined
Esailija
2
Chcesz używać jQuery czy nie?
arb
1
@ Zero21xxx Nie przejmuj się, ale to wymagałoby ode mnie przepisania całego powyższego kodu tak, aby używał obiektów JQuery zamiast standardowego JS.
Wesley
3
Napisałem tutajclassList kilka polyfillów .
Alex
1
@alex Twój post na blogu może zrobić aktualizację. Po pierwsze, ostatnie łącze (MDN) jest błędne, a querySelectorpowinno być classList. Po drugie, .removemetoda zawiera niepotrzebne wyrażenie RegExp, którego użycie - jak przyznałeś - wprowadza błąd. Bo już przed- i sufiksem przestrzenie, proste .replace(' ' + className + ' ', ' ')wystarczy (ponadto uwaga „Poprawna className nie powinny zawierać żadnych znaków specjalnych regex.” Jest nieprawidłowe, patrz specyfikacja (również prawdziwe dla HTML4))
Rob W

Odpowiedzi:

93

Ta classListwłaściwość nie jest obsługiwana przez IE9 i starsze. IE10 + obsługuje to jednak.
Użyj className += " .."zamiast tego. Uwaga: nie pomijaj spacji: nazwy klas powinny być dodawane w postaci listy oddzielonej spacjami.

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
źródło
3
@MikeChristensen classNameAtrybut istnieje zawsze. Jest inicjowany jako pusty ciąg. Aby ułatwić obsługę kodu, +=zaleca się użycie do dodawania nazw klas.
Rob W
2
w trybie ścisłym generuje to błąd, ponieważ nazwa_klasy jest tylko do odczytu.
Jason Aller
1
@JasonAller Jest to wysoce nieprawdopodobne, classNamewłaściwość elementu DOM to odczyt i zapis.
Rob W.
47
IE11 nadal zawiera błąd w classList, nie obsługuje classList w elemencie SVG.
codenamezero
1
a jQuery nie może również dodawać klas do elementów SVG, więc rozwiązanie z powyższego wydaje się być jedyną możliwą rzeczą do zrobienia, jeśli chcesz manipulować SVG.
Senthe
26

Jak wspomniano wcześniej, classListnie jest obsługiwany przez IE9 i starsze. Oprócz powyższej alternatywy Alexa, istnieje kilka polifillów, które mają na celu zastąpienie ich, tj. Po prostu umieść je na swojej stronie, a IE powinno po prostu działać (słynne ostatnie słowa!).

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

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

tagawa
źródło
11
Używam IE 11, a classList nie działa (działa w Chrome)
john ktejik
1
To dziwne - caniuse.com twierdzi, że powinno być obsługiwane przez IE10 +. Proponuję zapytać @TheWebJustWorks na Twitterze lub zgłosić
tagawa
3
@johnktejik Jeśli dodasz lub usuniesz wiele klas jednocześnie, nie będzie to działać w IE11, Firefox 24- i Chrome 24-. Czy to właśnie robiłeś?
mnsth
1
@BradAdams tutaj i tutaj . Caniuse i MDN również mają raporty. Jednak wszystkie te problemy zostały rozwiązane w Microsoft Edge!
mnsth
3
classList nadal nie jest obsługiwany w elementach SVG, te polyfills załatwią sprawę
Shoplifter.
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
źródło
2
+1 jednak: ele.getAttribute('class')w niektórych przypadkach zwracałem wartość null (może jeśli atrybut klasy nie jest jeszcze ustawiony?) - prosta instrukcja if rozwiązała problem.
Jeppe
9

W IE 10 i 11 właściwość classList jest zdefiniowana w HTMLElement.prototype.

Aby to działało na elementach SVG, właściwość powinna być zdefiniowana w Element.prototype, tak jak w innych przeglądarkach.

Bardzo małą poprawką byłoby skopiowanie dokładnego propertyDescriptor z HTMLElement.prototype do 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'));
    }
}
  • Musimy skopiować deskryptor, ponieważ Element.prototype.classList = HTMLElement.prototype.classListwyrzuciInvalid calling object
  • Pierwsza kontrola zapobiega nadpisywaniu właściwości w przeglądarkach, które obsługują natywnie.
  • Drugie sprawdzenie zapobiega błędom w wersjach IE wcześniejszych niż 9, gdzie HTMLElement nie jest jeszcze zaimplementowany, oraz w IE9, gdzie classList nie jest zaimplementowany.

Dla IE 8 i 9 użyj następującego kodu, dołączyłem również (zminimalizowany) polyfill dla Array.prototype.indexOf, ponieważ IE 8 nie obsługuje go natywnie (źródło polyfill : 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
źródło
Pierwszy kod dobrze go wyciął, ale brakuje dwóch nawiasów zamykających ).
superdweebie
@superdweebie, dzięki za uwagę. Poprawiłem swoją odpowiedź
Kevin Drost
8

Sprawdź to

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
})

i classList zadziała!

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

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

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

to wszystko!

asdru
źródło
Jestem noobem. Gdzie mam umieścić powyższe? W <script> czy w <style>?
Fandango68
Wstawiłem ten kod do bloku skryptu bezpośrednio nad błędnym kodem - działa bardzo dobrze. Dzięki.
robnick
1
Dobra robota, niestety to nie działa dla mnie we wszystkich przypadkach, znalazłem jednak ten: github.com/remy/polyfills/blob/…
Michiel
3

W Eksploratorze 11 classList.add działa tylko z pojedynczymi wartościami.

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

W tym przypadku Explorer dodaje tylko pierwszą klasę i ignoruje drugą. Więc musisz zrobić:

Element.classList.add("classOne");
Element.classList.add("classTwo");
Alonad
źródło
0

classListnie jest obsługiwany w IE <9. Użyj jQuery.addClass lub polyfill, takiego jak ten na https://developer.mozilla.org/en-US/docs/Web/API/Element/classList

Juan Mendes
źródło
1
Do każdego, kto to zlekceważył: to była prawda, kiedy opublikowano odpowiedź, i tylko IE 9 zaczął ją wspierać
Juan Mendes
2
Zaryzykowałbym przypuszczenie, że został odrzucony, ponieważ pytanie nie dotyczyło jQuery. Istnieją sposoby na obejście problemów, które nie wymagają użycia całej biblioteki JavaScript.
Vincent McNabb,
4
-1: tak ... dobrze ... dodajmy bibliotekę classList
100KB,
1
@ tereško Mówi "lub coś podobnego", jeśli nie używasz jeszcze jQuery. Zauważ, że zaakceptowana odpowiedź może dodać tę samą nazwę klasy dwukrotnie, ponieważ nie sprawdza, czy już istnieje.
Juan Mendes