Adding SVG class (className) support to jQuery

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)