Given:
<body>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<a xlink:href="url"></a>
</svg>
</body>
Is it possible to use the HTML DOM's .querySelector()
or .querySelectorAll()
to select the link inside the SVG by the contents of its xlink:href
attribute?
This works:
document.querySelector('a') // <a xlink:href="url"/>
These don't:
document.querySelector('[href="url"]') // null
document.querySelector('[xlink:href="url"]') // Error: not a valid selector
document.querySelector('[xlink\:href="url"]') // Error: not a valid selector
document.querySelector('[xlink\\:href="url"]') // null
Is there a way of writing that attribute selector to make it 'see' the xlink:href
?
[*|href]
will match both htmlhref
and svgxlink:href
, then use:not([href])
to exclude htmlhref
.tested in chrome
Query selector can handle namespaces, but it gets tricky because
The syntax for specifying namespaces in CSS selectors is different from html;
The querySelector API doesn't have any method for assigning a namespace prefix (like
xlink
) to an actual namespace (like"http://www.w3.org/1999/xlink"
).On the first point, the relevant part of the CSS specs allows you to specify no namespace (the default), a specific namespace, or any namespace:
See this fiddle, paying attention to the fill styles (default, hover, and active):
https://jsfiddle.net/eg43L/
The Selectors API adopts the CSS selector syntax, but has no equivalent to the
@namespace
rule for defining a namespace. As a result, selectors with namespaces are not valid but the wildcard namespace token is valid:(bold added)
Check out the fiddle again, this time paying attention to the console output. The command
document.querySelector('[*|href="#url"]')
returns the element you want.One final warning: MDN tells me that IE8- do not support CSS namespaces, so this might not work for them.
Update 2015-01-31:
As @Netsi1964 pointed out in the comments, this doesn't work for custom namespaced attributes in HTML 5 documents, since HTML doesn't support XML namespaces. (It would work in a stand-alone SVG or other XML document including XHTML.)
When the HTML5 parser encounters an attribute like
data:myAttribute="value"
it treats that as a single string for the attribute name, including the:
. To make things more confusing, it auto-lowercases the string.To get
querySelector
to select these attributes, you have to include thedata:
as part of the attribute string. However, since the:
has special meaning in CSS selectors, you need to escape it with a\
character. And since you need the\
to get passed through as part of the selector, you need to escape it in your JavaScript.The successful call therefore looks like:
To make things a little more logical, I would recommend using all lower-case for your attribute names, since the HTML 5 parser will convert them anyway. Blink/Webkit browser will auto-lowercase selectors you pass
querySelector
, but that's actually a very problematic bug (in means you can never select SVG elements with mixed-case tag names).But does the same solution work for
xlink:href
? No! The HTML 5 parser recognizesxlink:href
in SVG markup, and correctly parses it as a namespaced attribute.Here's the updated fiddle with additional tests. Again, look at the console output to see the results. Tested in Chrome 40, Firefox 35, and IE 11; the only difference in behavior is that Chrome matches the mixed-case selector.
Unfortunately not.
querySelector
doesn't handle XML namespaces, so there is no easy way to do this that way. You can however use anXPath
query.If you need full cross-browser support, such as for IE, which does not have a
document.evaluate
, you can polyfill it with wicked-good-xpath.Of course, depending on your usage, it may be easier to do this (which I think will work on IE):