javascript - etiqueta - jQuery SVG, ¿por qué no puedo agregar clase?
svg javascript (15)
Estoy usando jQuery SVG. No puedo agregar o quitar una clase a un objeto. ¿Alguien sabe mi error?
El SVG:
<rect class="jimmy" id="p5" x="200" y="200" width="100" height="100" />
El jQuery que no agregará la clase:
$(".jimmy").click(function() {
$(this).addClass("clicked");
});
Sé que SVG y jQuery están trabajando bien juntos porque puedo apuntar al objeto y disparar una alerta cuando se hace clic:
$(".jimmy").click(function() {
alert(''Handler for .click() called.'');
});
jQuery 3 no tiene este problema
Uno de los cambios enumerados en las revisiones de jQuery 3.0 es:
Una solución para este problema sería actualizar a jQuery 3. Funciona muy bien:
var flip = true;
setInterval(function() {
// Could use toggleClass, but demonstrating addClass.
if (flip) {
$(''svg circle'').addClass(''green'');
}
else {
$(''svg circle'').removeClass(''green'');
}
flip = !flip;
}, 1000);
svg circle {
fill: red;
stroke: black;
stroke-width: 5;
}
svg circle.green {
fill: green;
}
<script src="https://code.jquery.com/jquery-3.0.0.min.js"></script>
<svg>
<circle cx="50" cy="50" r="25" />
</svg>
El problema:
La razón por la que las funciones de manipulación de la clase jQuery no funcionan con los elementos SVG es porque las versiones de jQuery anteriores a 3.0 usan la propiedad className
para estas funciones.
Extracto de los attributes/classes.js
jQuery attributes/classes.js
:
cur = elem.nodeType === 1 && ( elem.className ?
( " " + elem.className + " " ).replace( rclass, " " ) :
" "
);
Esto se comporta como se espera para los elementos HTML, pero para los elementos SVG className
es un poco diferente. Para un elemento SVG, className
no es una cadena, sino una instancia de SVGAnimatedString
.
Considere el siguiente código:
var test_div = document.getElementById(''test-div'');
var test_svg = document.getElementById(''test-svg'');
console.log(test_div.className);
console.log(test_svg.className);
#test-div {
width: 200px;
height: 50px;
background: blue;
}
<div class="test div" id="test-div"></div>
<svg width="200" height="50" viewBox="0 0 200 50">
<rect width="200" height="50" fill="green" class="test svg" id="test-svg" />
</svg>
Si ejecuta este código, verá algo como lo siguiente en su consola de desarrollador.
test div
SVGAnimatedString { baseVal="test svg", animVal="test svg"}
Si tuviéramos que convertir ese objeto SVGAnimatedString
en una cadena como lo hace jQuery, tendríamos [object SVGAnimatedString]
, que es donde falla jQuery.
Cómo el plugin jQuery SVG maneja esto:
El complemento jQuery SVG soluciona este problema parcheando las funciones relevantes para agregar soporte SVG.
Extracto de jQuery SVG jquery.svgdom.js
:
function getClassNames(elem) {
return (!$.svg.isSVGElem(elem) ? elem.className :
(elem.className ? elem.className.baseVal : elem.getAttribute(''class''))) || '''';
}
Esta función detectará si un elemento es un elemento SVG, y si lo está, usará la propiedad SVGAnimatedString
objeto SVGAnimatedString
si está disponible, antes de SVGAnimatedString
al atributo de class
.
La postura histórica de jQuery sobre el tema:
jQuery actualmente lista este problema en su página No se arregla . Aquí están las partes relevantes.
SVG / VML o errores de elementos de espacio de nombre
jQuery es principalmente una biblioteca para el DOM de HTML, por lo que la mayoría de los problemas relacionados con documentos SVG / VML o elementos con espacios de nombre están fuera de alcance. Intentamos abordar los problemas que "sangran" en los documentos HTML, como los eventos que se desbordan de SVG.
Evidentemente, jQuery consideró el soporte completo de SVG fuera del alcance del núcleo de jQuery, y era más adecuado para los complementos.
jQuery 2.2 admite la manipulación de clases SVG
Las publicaciones publicadas de jQuery 2.2 y 1.12 incluyen la siguiente cita:
Si bien jQuery es una biblioteca HTML, acordamos que el soporte de clase para elementos SVG podría ser útil. Los usuarios ahora podrán llamar a los métodos .addClass () , .removeClass () , .toggleClass () y .hasClass () en SVG. jQuery ahora cambia el atributo de clase en lugar de la propiedad className . Esto también hace que los métodos de clase sean utilizables en documentos XML generales. Tenga en cuenta que muchas otras cosas no funcionarán con SVG, y aún recomendamos que utilice una biblioteca dedicada a SVG si necesita algo más allá de la manipulación de clases.
Ejemplo usando jQuery 2.2.0
Prueba:
- .addClass ()
- .removeClass ()
- .hasClass ()
Si hace clic en ese cuadrado pequeño, cambiará su color porque el atributo de class
se agrega / elimina.
$("#x").click(function() {
if ( $(this).hasClass("clicked") ) {
$(this).removeClass("clicked");
} else {
$(this).addClass("clicked");
}
});
.clicked {
fill: red !important;
}
<html>
<head>
<script src="https://code.jquery.com/jquery-2.2.0.js"></script>
</head>
<body>
<svg width="80" height="80">
<rect id="x" width="80" height="80" style="fill:rgb(0,0,255)" />
</svg>
</body>
</html>
Aquí está mi código poco elegante pero de trabajo que trata los siguientes problemas (sin ninguna dependencia):
-
classList
no existe en los elementos<svg>
en IE -
className
no representa el atributo declass
en los elementos<svg>
en IE -
getAttribute()
ysetAttribute()
rotas de IE antiguas
Utiliza classList
cuando sea posible.
Código:
var classNameContainsClass = function(fullClassName, className) {
return !!fullClassName &&
new RegExp("(?:^|//s)" + className + "(?://s|$)").test(fullClassName);
};
var hasClass = function(el, className) {
if (el.nodeType !== 1) {
return false;
}
if (typeof el.classList == "object") {
return (el.nodeType == 1) && el.classList.contains(className);
} else {
var classNameSupported = (typeof el.className == "string");
var elClass = classNameSupported ? el.className : el.getAttribute("class");
return classNameContainsClass(elClass, className);
}
};
var addClass = function(el, className) {
if (el.nodeType !== 1) {
return;
}
if (typeof el.classList == "object") {
el.classList.add(className);
} else {
var classNameSupported = (typeof el.className == "string");
var elClass = classNameSupported ?
el.className : el.getAttribute("class");
if (elClass) {
if (!classNameContainsClass(elClass, className)) {
elClass += " " + className;
}
} else {
elClass = className;
}
if (classNameSupported) {
el.className = elClass;
} else {
el.setAttribute("class", elClass);
}
}
};
var removeClass = (function() {
function replacer(matched, whiteSpaceBefore, whiteSpaceAfter) {
return (whiteSpaceBefore && whiteSpaceAfter) ? " " : "";
}
return function(el, className) {
if (el.nodeType !== 1) {
return;
}
if (typeof el.classList == "object") {
el.classList.remove(className);
} else {
var classNameSupported = (typeof el.className == "string");
var elClass = classNameSupported ?
el.className : el.getAttribute("class");
elClass = elClass.replace(new RegExp("(^|//s)" + className + "(//s|$)"), replacer);
if (classNameSupported) {
el.className = elClass;
} else {
el.setAttribute("class", elClass);
}
}
}; //added semicolon here
})();
Ejemplo de uso:
var el = document.getElementById("someId");
if (hasClass(el, "someClass")) {
removeClass(el, "someClass");
}
addClass(el, "someOtherClass");
Basándome en las respuestas anteriores, creé la siguiente API
/*
* .addClassSVG(className)
* Adds the specified class(es) to each of the set of matched SVG elements.
*/
$.fn.addClassSVG = function(className){
$(this).attr(''class'', function(index, existingClassNames) {
return ((existingClassNames !== undefined) ? (existingClassNames + '' '') : '''') + className;
});
return this;
};
/*
* .removeClassSVG(className)
* Removes the specified class to each of the set of matched SVG elements.
*/
$.fn.removeClassSVG = function(className){
$(this).attr(''class'', function(index, existingClassNames) {
var re = new RegExp(''//b'' + className + ''//b'', ''g'');
return existingClassNames.replace(re, '''');
});
return this;
};
Después de cargar jquery.svg.js
, debe cargar este archivo: http://keith-wood.name/js/jquery.svgdom.js
.
Fuente: http://keith-wood.name/svg.html#dom
Ejemplo de trabajo: http://jsfiddle.net/74RbC/99/
Edición 2016: lee las siguientes dos respuestas.
- JQuery 3 soluciona el problema subyacente
- Vanilla JS:
element.classList.add(''newclass'')
funciona en los navegadores modernos
JQuery (menos de 3) no puede agregar una clase a un SVG.
.attr()
funciona con SVG, así que si quieres depender de jQuery:
// Instead of .addClass("newclass")
$("#item").attr("class", "oldclass newclass");
// Instead of .removeClass("newclass")
$("#item").attr("class", "oldclass");
Y si no quieres depender de jQuery:
var element = document.getElementById("item");
// Instead of .addClass("newclass")
element.setAttribute("class", "oldclass newclass");
// Instead of .removeClass("newclass")
element.setAttribute("class", "oldclass");
Escribí esto en mi proyecto, y funciona ... probablemente;)
$.fn.addSvgClass = function(className) {
var attr
this.each(function() {
attr = $(this).attr(''class'')
if(attr.indexOf(className) < 0) {
$(this).attr(''class'', attr+'' ''+className+ '' '')
}
})
};
$.fn.removeSvgClass = function(className) {
var attr
this.each(function() {
attr = $(this).attr(''class'')
attr = attr.replace(className , '' '')
$(this).attr(''class'' , attr)
})
};
ejemplos
$(''path'').addSvgClass(''fillWithOrange'')
$(''path'').removeSvgClass(''fillWithOrange'')
Hay element.classList en la API de DOM que funciona para los elementos HTML y SVG. No hay necesidad de jQuery SVG plugin o incluso jQuery.
$(".jimmy").click(function() {
this.classList.add("clicked");
});
Inspirado por las respuestas anteriores, especialmente por Sagar Gala, he creado esta API . Puede usarlo si no quiere o no puede actualizar su versión jquery.
O simplemente use métodos DOM de la vieja escuela cuando JQ tiene un mono en el medio en algún lugar.
var myElement = $(''#my_element'')[0];
var myElClass = myElement.getAttribute(''class'').split(//s+/g);
//splits class into an array based on 1+ white space characters
myElClass.push(''new_class'');
myElement.setAttribute(''class'', myElClass.join('' ''));
//$(myElement) to return to JQ wrapper-land
Aprende la gente de DOM. Incluso en el marco-palooza de 2016 ayuda bastante regularmente. Además, si alguna vez escuchas a alguien comparar el DOM con el ensamblaje, dame una patada.
Si tiene clases dinámicas o no sabe qué clases ya podrían aplicarse, entonces este método creo que es el mejor enfoque:
// addClass
$(''path'').attr(''class'', function(index, classNames) {
return classNames + '' class-name'';
});
// removeClass
$(''path'').attr(''class'', function(index, classNames) {
return classNames.replace(''class-name'', '''');
});
Solo agregue el constructor de prototipo faltante a todos los nodos SVG:
SVGElement.prototype.hasClass = function (className) {
return new RegExp(''(//s|^)'' + className + ''(//s|$)'').test(this.getAttribute(''class''));
};
SVGElement.prototype.addClass = function (className) {
if (!this.hasClass(className)) {
this.setAttribute(''class'', this.getAttribute(''class'') + '' '' + className);
}
};
SVGElement.prototype.removeClass = function (className) {
var removedClass = this.getAttribute(''class'').replace(new RegExp(''(//s|^)'' + className + ''(//s|$)'', ''g''), ''$2'');
if (this.hasClass(className)) {
this.setAttribute(''class'', removedClass);
}
};
Entonces puedes usarlo de esta manera sin requerir jQuery:
this.addClass(''clicked'');
this.removeClass(''clicked'');
Todo el crédito es para toddmotto.com/… .
Una solución alternativa podría ser agregar Clase a un contenedor del elemento svg:
$(''.svg-container'').addClass(''svg-red'');
.svg-red svg circle{
fill: #ED3F32;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="svg-container">
<svg height="40" width="40">
<circle cx="20" cy="20" r="20"/>
</svg>
</div>
Yo uso Snap.svg para agregar una clase a y SVG.
var jimmy = Snap(" .jimmy ")
jimmy.addClass("exampleClass");
jQuery no admite las clases de elementos SVG. Puede obtener el elemento directamente $(el).get(0)
y usar classList
y add / remove
. Hay un truco con esto también en que el elemento SVG superior es en realidad un objeto DOM normal y puede usarse como cualquier otro elemento en jQuery. En mi proyecto, creé esto para ocuparme de lo que necesitaba, pero la documentación proporcionada en la Red de Desarrolladores de Mozilla tiene un complemento que puede usarse como alternativa.
function addRemoveClass(jqEl, className, addOrRemove)
{
var classAttr = jqEl.attr(''class'');
if (!addOrRemove) {
classAttr = classAttr.replace(new RegExp(''//s?'' + className), '''');
jqEl.attr(''class'', classAttr);
} else {
classAttr = classAttr + (classAttr.length === 0 ? '''' : '' '') + className;
jqEl.attr(''class'', classAttr);
}
}
Una alternativa más difícil es usar D3.js como su motor de selección en su lugar. Mis proyectos tienen gráficos que se crean con él, por lo que también está en el ámbito de mi aplicación. D3 modifica correctamente los atributos de clase de elementos DOM de vainilla y elementos SVG. Aunque agregar D3 solo para este caso probablemente sería excesivo.
d3.select(el).classed(''myclass'', true);