3

I'm trying to dynamically rotate a gradient in an SVG based on mouse position and it's all working fine except this one line. The trouble is I can't seem to get the jQuery attr method to work. setAttribute works fine to replace rotate(#,#,#) inside of the attribute gradientTransform but attr doesn't. The part that I'm really having trouble with is that I can change other attributes with attr (as in the third example in the fiddle).

http://jsfiddle.net/samt/8yUNL/

this one works

mainLogoFill[0].setAttribute('gradientTransform', 'rotate(45,100,100)');

this one doesn't

mainLogoFill.attr('gradientTransform', 'rotate(90,100,100)');

this one throws me.. why does it work?

mainLogoFill.attr('x1', '100');
chrylis -cautiouslyoptimistic-
  • 67,584
  • 19
  • 106
  • 140
sam
  • 243
  • 2
  • 8
  • @jfriend00 nope! prop() method also doesn't work. – Bhojendra Rauniyar Mar 28 '14 at 04:18
  • possible duplicate of [Does the attr() in jQuery force lowercase?](http://stackoverflow.com/questions/12361971/does-the-attr-in-jquery-force-lowercase) – Ja͢ck Mar 28 '14 at 04:28
  • SVG is based on XML, so [*attribute names are case sensitive*](http://www.w3.org/TR/SVG11/styling.html#CaseSensitivity). – RobG Mar 28 '14 at 04:34
  • @RobG Yeah, jQuery tries to detect whether it's in XML mode, but does a suboptimal job at it ;-) – Ja͢ck Mar 28 '14 at 04:35

2 Answers2

2

jQuery uses toLowerCase with attribute names, so it ends up as gradienttransform not gradientTransform, which is a new attribute and not the same as the one you already have, hence the issues.

The attr() method starts like this

function (elem, name, value) {
    var hooks, ret, nType = elem.nodeType;

    // don't get/set attributes on text, comment and attribute nodes
    if (!elem || nType === 3 || nType === 8 || nType === 2) {
        return;
    }

    // Fallback to prop when attributes are not supported
    if (typeof elem.getAttribute === core_strundefined) {
        return jQuery.prop(elem, name, value);
    }

    // All attributes are lowercase
    // Grab necessary hook if one is defined
    if (nType !== 1 || !jQuery.isXMLDoc(elem)) {
        name = name.toLowerCase(); // this is the line where the name is lowercased
        hooks = jQuery.attrHooks[name] || (jQuery.expr.match.bool.test(name) ? boolHook : nodeHook);
    }

    if (value !== undefined) {

The way around this is to use setAttribute instead.

adeneo
  • 293,187
  • 26
  • 361
  • 361
0

Although SVG is based on XML (and thus uses case sensitive attribute names), jQuery's detection fails at this point and transforms attribute names to lower case.

If you don't like using .setAttribute() everywhere, an alternative is to patch jQuery by overriding how it determines XML mode:

jQuery.isXMLDoc = function(elem)
{
    return (elem.ownerDocument || elem).documentElement.nodeName !== "HTML" || 
        (elem.namespaceURI).indexOf('html') == -1;
}

The above code additionally checks if the namespace of the current element has the term "html" in it; the namespace for SVG is "http://www.w3.org/2000/svg", so jQuery will not alter the case of your property names.

Demo

Ja͢ck
  • 161,074
  • 33
  • 239
  • 294
  • Though that is really an "isHTMLDoc" method. ;-) Don't get too cocky though, IE thinks a plain HTML document is XHTML (http://www.w3.org/1999/xhtml), which is case sensitive and requires HTML attribute tag and names in lower case, yet returns values for HTML tag names in upper case. C'est la vie. :-/ – RobG Mar 28 '14 at 06:16
  • @RobG Isn't it more like an "is not html doc" method? :) – Ja͢ck Mar 28 '14 at 06:24
  • —err, yes. I was thinking it *needs* an isHTMLDoc method, but that's not what came out. :-( – RobG Mar 28 '14 at 06:36