Wex
Wex

Reputation: 15695

Cannot select SVG element by class unless using attribute selector

For some reason, $('#p-foo').siblings('.bar') works, but $('#g-foo').siblings('.bar') does not. Can anyone point out to me why this is the case?

<div>
    <p id="p-foo"></p>
    <p class="bar"></p>
</div>

<svg>
    <g id="g-foo"></g>
    <g class="bar"></g>
</svg>

I wrote a quick test script to demonstrate the problem:

var selector  = '.bar',
    attribute = '[class=bar]';

var check = function (foo, d) {
  console.log(d + ' matches ' + foo.siblings(d).length);
};

var p_foo = $('#p-foo');
console.log('DIV');
check(p_foo, selector);
check(p_foo, attribute);

var g_foo = $('#g-foo');
console.log('SVG');
check(g_foo, selector);
check(g_foo, attribute);

And the output is:

DIV
.bar matches 1
[class=bar] matches 1
SVG
.bar matches 0
[class=bar] matches 1 

Upvotes: 1

Views: 804

Answers (1)

nrabinowitz
nrabinowitz

Reputation: 55688

I thought this was an interesting problem, so I did a little source code research.

.siblings() calls $.filter, which delegates to Sizzle and eventually uses the filters here.

  • If you use the [class=bar] selector, Sizzle uses the ATTR filter, which simply retrieves the attribute and does an equality check against its value. This works for both p and g.

  • If you use the .bar selector, Sizzle uses the CLASS filter, which uses a regex against elem.className. In SVG elements, however, className is not a string, but an instance of SVGAnimatedString (which, to be perfectly honest, I've never fully wrapped my head around, but it seems to have both an animValue and a baseValue). The regex fails, and the element isn't matched.

I believe Sizzle has additional handling that might make this work if the entire document is SVG or XML, but because this SVG is embedded in an HTML doc, that doesn't kick in.

Upvotes: 3

Related Questions