Adding SVG class (className) support to jQuery
Overview of the problem: The HTML and SVG DOM extensions have
differing interface definitions for "className". For the HTML DOM,
className is a DOMString [1]. For SVG, className is defined as type
SVGAnimatedString [2] (instead of good old DOMString) in SVGStylable
[3]. (Furthermore, not all SVG elements inherit from SVGStylable - a
fact which some Mozilla devs think is ridiculous, and I'd tend to
agree, and they plan to add support for class everywhere in spite of
the W3C recommendation [4]). To access the class value of an SVG
element, you must call elem.className.baseVal (or elem.getAttribute
("class")).
So, I'd like to tackle this problem as either a modification of jQuery
or a plugin to jQuery. The following jQuery interfaces would need to
be changed:
- addClass/hasClass/removeClass/toggleClass
- Selection: The Sizzle backend for allowing selection based on class
would need to be changed a little
Keith Wood has provided a jQuery plugin which solves part of this
problem by creating a wrapper around addClass, et al. [5]. But his
plugin doesn't address selection of SVG elements by class name (e.g. $
(".my-svg-class")).
My question for the devs: is a modification to jQuery itself
necessitated or can/should I factor this out into a separate plugin?
It seems that I have to drill to the bottom layers of Sizzle, which
makes me think a plugin is not ideal, and I would appreciate any
suggestions.
Here's a patch I came up with (alternatively, it should be available
here for a month: http://pastebin.com/m24bdbbd9):
--- jquery-1.3.2.js 2009-05-25 12:00:43.667854804 -0600
+++ jquery-1.3.2-modified-selectors.js 2009-05-26 22:52:33.748668152
-0600
@@ -707,27 +707,54 @@
},
className: {
+
+ get: function( elem ) {
+ //var classNames = elem.className.baseVal || elem.className ||
elem.getAttribute("class");
+ var classNames = elem.getAttribute("class");
+ classNames = classNames ? classNames : "";
+ return classNames
+ },
+
+ set: function( elem, value ) {
+ elem.setAttribute("class", value);
+ },
+
+
// internal only, use addClass("class")
add: function( elem, classNames ) {
jQuery.each((classNames || "").split(/\s+/), function(i, className)
{
- if ( elem.nodeType == 1 && !jQuery.className.has( elem.className,
className ) )
- elem.className += (elem.className ? " " : "") + className;
+ var curClassName = jQuery.className.get(elem);
+ if ( elem.nodeType == 1 && !jQuery.className.has( curClassName,
className ) )
+ jQuery.className.set(elem, curClassName + (curClassName ? " " :
"") + className);
});
},
// internal only, use removeClass("class")
remove: function( elem, classNames ) {
if (elem.nodeType == 1)
- elem.className = classNames !== undefined ?
- jQuery.grep(elem.className.split(/\s+/), function(className){
+ jQuery.className.set(elem, classNames !== undefined ?
+ jQuery.grep(jQuery.className.get(elem).split(/\s+/), function
(className){
return !jQuery.className.has( classNames, className );
}).join(" ") :
- "";
+ ""
+ );
},
// internal only, use hasClass("class")
has: function( elem, className ) {
- return elem && jQuery.inArray( className, (elem.className ||
elem).toString().split(/\s+/) ) > -1;
+ var classNames = "";
+ if (elem)
+ {
+ if (elem.getAttribute)
+ {
+ classNames = elem.jQuery.className.get(elem);
+ }
+ else
+ {
+ classNames = elem; // <-- ???
+ }
+ }
+ return elem && jQuery.inArray( className, classNames.toString
().split(/\s+/) ) > -1;
}
},
@@ -1779,7 +1806,8 @@
for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
if ( elem ) {
- if ( not ^ (elem.className && (" " + elem.className + "
").indexOf(match) >= 0) ) {
+ var className = jQuery.className.get(elem);
+ if ( not ^ (className && (" " + className + " ").indexOf(match)