Код с classList не работает в IE?

83

Я использую следующий код, но в IE он не работает. Это сообщение:

Невозможно получить значение свойства 'добавить': объект равен нулю или не определен "

Я предполагаю, что это просто проблема поддержки IE. Как бы вы заставили следующий код работать в IE?

Есть идеи?

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);
Уэсли
источник
5
IE не имеет classList, следовательно, он равен null или undefined
Esailija
2
Вы хотите использовать jQuery или нет?
arb
1
@ Zero21xxx Не обращайте на это внимания, но для этого мне потребуется переписать весь приведенный выше код, чтобы использовать объекты JQuery вместо стандартного JS.
Wesley
3
Я написал здесьclassList несколько полифилов для .
Alex
1
@alex Ваше сообщение в блоге может быть обновлено. Во-первых, последняя ссылка (MDN) неверна, querySelectorдолжна быть classList. Во-вторых, .removeметод содержит ненужное RegExp, использование которого, как вы уже признали, приводит к ошибке. Поскольку у вас уже есть предварительные и суффиксные пробелы, достаточно простого .replace(' ' + className + ' ', ' ')(кроме того, замечание «Допустимое имя класса не должно содержать никаких специальных символов регулярного выражения.» Неверно, см. Спецификацию (также верно для HTML4))
Роб В.

Ответы:

93

classListСвойство не поддерживается IE9 и ниже. Хотя IE10 + поддерживает это. Вместо этого
используйте className += " ..". Примечание. Не пропускайте пробел: имена классов следует добавлять в список, разделенный пробелами.

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);
Роб В
источник
3
@MikeChristensen classNameАтрибут существует всегда. Он инициализируется пустой строкой. Для упрощения обслуживания кода +=рекомендуется использовать для добавления имен классов.
Rob W
2
в строгом режиме это вызывает ошибку, поскольку className доступен только для чтения.
Джейсон Аллер
1
@JasonAller Это маловероятно, classNameсвойство элемента DOM - чтение-запись.
Rob W
47
IE11 по-прежнему ошибается в classList, он не поддерживает classList в элементе SVG.
codenamezero
1
и jQuery не может добавлять классы к элементам SVG, поэтому решение, приведенное выше, похоже, единственно возможное, если вы хотите манипулировать SVG.
Senthe
26

Как упоминалось другими, classList не поддерживается IE9 и старше. Помимо альтернативы Алекса, приведенной выше, есть пара полифилов, которые предназначены для замены, то есть просто добавьте их на свою страницу, и IE должен просто работать (известные последние слова!).

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

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

Tagawa
источник
11
Я использую IE 11, и classList, похоже, не работает (работает в хроме)
Джон Ктеджик
1
Это странно - caniuse.com говорит, что он должен поддерживаться IE10 +. Я предлагаю спросить @TheWebJustWorks в Twitter или зарегистрировать проблему на webcompat.com
tagawa
3
@johnktejik, если вы добавляете или удаляете сразу несколько классов, он не будет работать в IE11, Firefox 24- и Chrome 24-. Это то, что вы делали?
пнс
1
@BradAdams Здесь и здесь . Caniuse и MDN также имеют отчеты. Однако все эти проблемы исправлены в Microsoft Edge!
пнс
3
classList по-прежнему не поддерживается в элементах SVG, эти полифилы делают свое дело
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, ' '));
    }
}
Ритеш Кумар
источник
2
+1, однако: ele.getAttribute('class')в некоторых случаях я возвращал null (может быть, если атрибут class еще не установлен?) - простая инструкция if решила эту проблему.
Jeppe
9

В IE 10 и 11 свойство classList определено в HTMLElement.prototype.

Чтобы заставить его работать с SVG-элементами, свойство должно быть определено в Element.prototype, как это было в других браузерах.

Очень маленьким исправлением было бы копирование точного объекта propertyDescriptor из HTMLElement.prototype в 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'));
    }
}
  • Нам нужно скопировать дескриптор, так как Element.prototype.classList = HTMLElement.prototype.classListвыброситInvalid calling object
  • Первая проверка предотвращает перезапись свойства в браузерах, которые изначально поддерживаются.
  • Вторая проверка предотвращает ошибки в версиях IE до 9, где HTMLElement еще не реализован, и в IE9, где classList не реализован.

Для IE 8 и 9 используйте следующий код, я также включил (уменьшенный) полифилл для Array.prototype.indexOf, потому что IE 8 не поддерживает его изначально (источник полифила : 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;
    }
});
Кевин Дрост
источник
В первом коде это хорошо, но отсутствуют две закрывающие скобки ).
superdweebie 01
@superdweebie, спасибо, что заметили. Я исправил свой ответ
Кевин Дрост
8

Проверь это

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

и classList будет работать!

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

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

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

это все!

Asdru
источник
Я новичок. Где мне разместить вышеперечисленное? В <script> или в <style>?
Fandango68,
Я вставил этот код в блок сценария непосредственно над ошибочным кодом - работает очень хорошо. Благодарю.
robnick 06
1
Хорошая работа, к сожалению, это не срабатывает для меня во всех случаях, однако я нашел вот это: github.com/remy/polyfills/blob/…
Michiel
3

В Explorer 11 classList.add работает только с отдельными значениями.

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

В этом случае Explorer добавляет только первый класс и игнорирует второй. Итак, нужно сделать:

Element.classList.add("classOne");
Element.classList.add("classTwo");
Алонад
источник
0

classListне поддерживается в IE <9. Используйте jQuery.addClass или полифилл, подобный тому, который находится на https://developer.mozilla.org/en-US/docs/Web/API/Element/classList

Хуан Мендес
источник
1
Тем, кто проголосовал против этого: это было правдой, когда был опубликован ответ, и только IE 9 начал его поддерживать
Хуан Мендес
2
Рискну предположить, что он был отклонен, потому что вопрос не касался jQuery. Есть способы обойти проблемы, которые не связаны с использованием всей библиотеки Javascript.
Винсент Макнабб
4
-1: ага .. верно ... давайте добавим библиотеку 100 classList
КБ
1
@ tereško Там написано «или что-то в этом роде», если вы еще не используете jQuery. Обратите внимание, что принятый ответ может добавить одно и то же имя класса дважды, поскольку он не проверяет, существует ли он уже.
Хуан Мендес