¿El código con classList no funciona en IE?

83

Estoy usando el siguiente código, pero falla en IE. El mensaje es:

No se puede obtener el valor de la propiedad 'agregar': el objeto es nulo o no está definido "

Supongo que esto es solo un problema de soporte de IE. ¿Cómo haría que funcione el siguiente código en IE?

¿Algunas ideas?

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
fuente
5
IE no tiene classList, por lo tanto, es nulo o indefinido
Esailija
2
¿Quieres usar jQuery o no?
ARB
1
@ Zero21xxx No me importa, pero eso me obligaría a reescribir todo el código anterior para usar objetos JQuery en lugar de JS estándar.
Wesley
3
Escribí algunos polyfills aquí paraclassList .
alex
1
@alex Su publicación de blog podría hacer una actualización. Primero, el enlace final (MDN) es incorrecto, querySelectordebería serlo classList. En segundo lugar, el .removemétodo contiene una RegExp innecesaria, cuyo uso -como ha reconocido- introduce un error. Debido a que ya tiene espacios prefijados y con sufijos, un simple .replace(' ' + className + ' ', ' ')es suficiente (además, la observación "Un className válido no debe contener ningún carácter especial de expresiones regulares" es incorrecta, consulte las especificaciones (también es cierto para HTML4))
Rob W

Respuestas:

92

La classListpropiedad no es compatible con IE9 y versiones anteriores. Sin embargo, IE10 + lo admite.
Úselo en su className += " .."lugar. Nota: No omita el espacio: los nombres de las clases deben agregarse en una lista separada por espacios en blanco.

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
fuente
3
@MikeChristensen El classNameatributo siempre existe. Se inicializa en una cadena vacía. Para facilitar el mantenimiento del código, +=se recomienda usar para agregar nombres de clases.
Rob W
2
en modo estricto, esto genera un error porque className es de solo lectura.
Jason Aller
1
@JasonAller Eso es muy poco probable. La classNamepropiedad de un elemento DOM es lectura-escritura.
Rob W
47
IE11 todavía tiene errores en classList, no es compatible con classList en el elemento SVG.
codenamezero
1
y jQuery tampoco puede agregar clases a los elementos SVG, por lo que la solución de arriba parece ser la única cosa posible si desea manipular SVG.
Senthe
26

Como han mencionado otros, classListno es compatible con IE9 y versiones anteriores. Además de la alternativa de Alex anterior, hay un par de polyfills que pretenden ser un reemplazo directo, es decir, simplemente inclúyalos en su página e IE debería funcionar (¡las famosas últimas palabras!).

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

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

tagawa
fuente
11
Estoy usando IE 11 y classList no parece estar funcionando (funciona en Chrome)
john ktejik
1
Eso es extraño: caniuse.com dice que debería ser compatible con IE10 +. Sugiero preguntar a @TheWebJustWorks en Twitter o presentar un problema en webcompat.com
tagawa
3
@johnktejik si agrega o elimina varias clases a la vez, no funcionará en IE11, Firefox 24- y Chrome 24-. ¿Era eso lo que estabas haciendo?
mes
1
@BradAdams Aquí y aquí . Caniuse y MDN también tienen informes. ¡Sin embargo, todos esos problemas están solucionados en Microsoft Edge!
mes
3
classList todavía no es compatible con elementos SVG, estos polyfills hacen el truco
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
fuente
2
+1, sin embargo: he ele.getAttribute('class')devuelto nulo en algunos casos (¿tal vez si el atributo de clase aún no está configurado?) - una simple declaración if lo resolvió.
Jeppe
8

mira esto

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

¡y classList funcionará!

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

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

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

¡eso es todo!

asdru
fuente
Soy un novato. ¿Dónde coloco lo anterior? ¿En el <script> o en el <style>?
Fandango68
Inserté este código en un bloque de script inmediatamente encima del código ofensivo; funciona muy bien. Gracias.
robnick
1
Buen trabajo, desafortunadamente esto no funciona para mí en todos los casos, sin embargo encontré este: github.com/remy/polyfills/blob/…
Michiel
8

En IE 10 y 11, la propiedad classList se define en HTMLElement.prototype.

Para que funcione en elementos SVG, la propiedad debe definirse en Element.prototype, como lo ha sido en otros navegadores.

Una solución muy pequeña sería copiar el propertyDescriptor exacto de HTMLElement.prototype a 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'));
    }
}
  • Necesitamos copiar el descriptor, ya Element.prototype.classList = HTMLElement.prototype.classListque arrojaráInvalid calling object
  • La primera comprobación evita que se sobrescriba la propiedad en los navegadores que la admiten de forma nativa.
  • La segunda verificación es evitar errores en las versiones de IE anteriores a la 9, donde HTMLElement aún no está implementado, y en IE9 donde no se implementa classList.

Para IE 8 y 9, use el siguiente código, también incluí un polyfill (minificado) para Array.prototype.indexOf, porque IE 8 no lo admite de forma nativa (fuente de 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
fuente
El primer código lo recortó bien, pero faltan dos corchetes de cierre ).
superdweebie
@superdweebie, gracias por darte cuenta. He arreglado mi respuesta
Kevin Drost
3

En Explorer 11, classList.add funciona solo con valores individuales.

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

En este caso, Explorer agrega solo la primera clase e ignora la segunda. Entonces necesito hacer:

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

classListno es compatible con IE <9. Use jQuery.addClass o un polyfill como el de https://developer.mozilla.org/en-US/docs/Web/API/Element/classList

Juan Mendes
fuente
1
Para quienes votaron en contra: esto era cierto cuando se publicó la respuesta, y solo IE 9 comenzó a respaldarla
Juan Mendes
2
Me arriesgaría a adivinar que se votó en contra porque la pregunta no solicitó jQuery. Hay formas de solucionar problemas que no implican el uso de una biblioteca Javascript completa.
Vincent McNabb
4
-1: sí ... cierto ... agreguemos una biblioteca de 100 KB para imitar mal la classListfuncionalidad
tereško
1
@ tereško Dice "o algo parecido", en caso de que no estés usando jQuery. Tenga en cuenta que la respuesta aceptada podría agregar el mismo nombre de clase dos veces, ya que no verifica si ya existe.
Juan Mendes