I am using jQuery SVG. I can\'t add or remove a class to an object. Anyone know my mistake?
The SVG:
There is element.classList in the DOM API that works for both HTML and SVG elements. No need for jQuery SVG plugin or even jQuery.
$(".jimmy").click(function() {
this.classList.add("clicked");
});
One workaround could be to addClass to a container of the svg element:
$('.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>
One of the changes listed on the jQuery 3.0 revisions is:
add SVG class manipulation (#2199, 20aaed3)
One solution for this issue would be to upgrade to jQuery 3. It works great:
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>
The reason the jQuery class manipulation functions do not work with the SVG elements is because jQuery versions prior to 3.0 use the className property for these functions.
cur = elem.nodeType === 1 && ( elem.className ?
( " " + elem.className + " " ).replace( rclass, " " ) :
" "
);
This behaves as expected for HTML elements, but for SVG elements className
is a little different. For an SVG element, className
is not a string, but an instance of SVGAnimatedString.
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>
If you run this code you will see something like the following in your developer console.
test div
SVGAnimatedString { baseVal="test svg", animVal="test svg"}
If we were to cast that SVGAnimatedString
object to a string as jQuery does, we would have [object SVGAnimatedString]
, which is where jQuery fails.
The jQuery SVG plugin works around this by patching the relevant functions to add SVG support.
function getClassNames(elem) {
return (!$.svg.isSVGElem(elem) ? elem.className :
(elem.className ? elem.className.baseVal : elem.getAttribute('class'))) || '';
}
This function will detect if an element is an SVG element, and if it is it will use the baseVal
property of the SVGAnimatedString
object if available, before falling back on the class
attribute.
jQuery currently lists this issue on their Won’t Fix page. Here is the relevant parts.
SVG/VML or Namespaced Elements Bugs
jQuery is primarily a library for the HTML DOM, so most problems related to SVG/VML documents or namespaced elements are out of scope. We do try to address problems that "bleed through" to HTML documents, such as events that bubble out of SVG.
Evidently jQuery considered full SVG support outside the scope of the jQuery core, and better suited for plugins.
Just add the missing prototype constructor to all SVG nodes:
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);
}
};
You can then use it this way without requiring jQuery:
this.addClass('clicked');
this.removeClass('clicked');
All credit goes to Todd Moto.
I wrote this in my project, and it works... probably;)
$.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)
})
};
examples
$('path').addSvgClass('fillWithOrange')
$('path').removeSvgClass('fillWithOrange')
The jQuery 2.2 and 1.12 Released post includes the following quote:
While jQuery is a HTML library, we agreed that class support for SVG elements could be useful. Users will now be able to call the .addClass(), .removeClass(), .toggleClass(), and .hasClass() methods on SVG. jQuery now changes the class attribute rather than the className property. This also makes the class methods usable in general XML documents. Keep in mind that many other things will not work with SVG, and we still recommend using a library dedicated to SVG if you need anything beyond class manipulation.
It tests:
If you click on that small square, it will change its color because the class
attribute is added / removed.
$("#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>