How to Use CSS Attribute Selector for an Svg Element with Namespaced Attribute Href

How to use CSS attribute selector for an SVG element with namespaced attribute href?

Demo Fiddle

Firstly, in order to use xlink slectors, you need to to declare the xlink namespace at the top of your stylesheet according to the spec:

@namespace xlink 'http://www.w3.org/1999/xlink';

Then, you can use the following attribute selector with a namespace prefix:

svg image[xlink|href*="temp"] {
outline: 3px solid red;
}

The attribute name in an attribute selector is given as a CSS
qualified name: a namespace prefix that has been previously declared
may be prepended to the attribute name separated by the namespace
separator "vertical bar" (|). In keeping with the Namespaces in the
XML recommendation

Do CSS namespace attribute selectors work with XHTML/HTML elements?

They do. You've just set up either your markup or your CSS rules incorrectly.

Your attribute selectors are looking for elements with href attributes (in the respective namespaces), but your <p> elements have xref attributes, not href attributes, so they don't match.

Your <xyz:p> and <xyz> elements on the other hand all have href attributes, so those are the ones that end up matching your attribute selectors instead.

Is it possible to use HTML's .querySelector() to select by xlink attribute in an SVG?

Query selector can handle namespaces, but it gets tricky because

  1. The syntax for specifying namespaces in CSS selectors is different from html;

  2. 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:

@namespace foo "http://www.example.com";
[foo|att=val] { color: blue }
[*|att] { color: yellow }
[|att] { color: green }
[att] { color: green }

The first rule will match only elements with the attribute att in the "http://www.example.com" namespace with the value "val".

The second rule will match only elements with the attribute att regardless of the namespace of the attribute (including no namespace).

The last two rules are equivalent and will match only elements with the attribute att where the attribute is not in a 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:

If the group of selectors include namespace prefixes that need to be resolved, the implementation must raise a SYNTAX_ERR exception ([DOM-LEVEL-3-CORE], section 1.4).

This specification does not provide support for resolving arbitrary namespace prefixes. However, support for a namespace prefix resolution mechanism may be considered for inclusion in a future version of this specification.

A namespace prefix needs to be resolved if the namespace component is neither empty (e.g. |div), representing the null namespace, or an asterisk (e.g. *|div), representing any namespace. Since the asterisk or empty namespace prefix do not need to be resolved, implementations that support the namespace syntax in Selectors must support these.

(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 the data: 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:

document.querySelector('[data\\:myattribute="value"]');

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 recognizes xlink: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.

Selecting an element by its attribute when it has a colon in its name

I've never used Chartist, but judging by the ct: namespace prefix, it appears to be an extension to SVG markup. So you're no longer really dealing with HTML here; you're dealing with XML, because SVG is an XML-based markup language.

Escaping the colon or otherwise specifying it as part of the attribute name doesn't work because the ct: no longer becomes part of the attribute name when it's treated like a namespace prefix (which is what it is). In a regular HTML document, an attribute name like ct:series-name would indeed include the prefix, because namespace prefixes only have special meaning in XML, not in HTML.

Anyway, the web inspector shows the following XML for your svg start tag:

<svg class="ct-chart-bar" xmlns:ct="http://gionkunz.github.com/chartist-js/ct" width="100%" height="100%" style="width: 100%; height: 100%;">

What you need to do is reflect this XML namespace in your CSS using a @namespace rule:

@namespace ct 'http://gionkunz.github.com/chartist-js/ct';

And, rather than escaping the colon, use a pipe to indicate a namespace prefix:

[ct|series-name='second'] .ct-bar {
stroke: black;
stroke-width: 20px;
stroke-linecap: round;
}

And it should work as expected.

Selecting elements with specific namespaced attributes

Use XPath, specifying the namespace for the g elements. Since your root element declares the xmlns:svg to be the same as the new default namespace (xmlns) you can use svg as your prefix:

require 'nokogiri'
doc = Nokogiri.XML(IO.read('contents.xml'))
layers = doc.xpath('//svg:g[@inkscape:groupmode="layer"]')

p layers.map{ |layer| layer['id'] }
#=> ["layer1", "g2818"]

Decoded, the above XPath says:

  • // - At any level of the document
  • svg:g - …find g elements with a namespace matching the svg namespace
  • […] - …but only if the contents of this are met
  • @inkscape:groupmode - …there is an attribute (@) named groupmode with a namespace matching inkscape
  • ="layer" - and the intrinsic value of this attribute is the text layer.

Alternatively, if you're just trying to read this file (and not manipulate and re-save it) you can use the gross-but-simplifying hack of removing all namespaces. In this case, your original code works simply:

doc.remove_namespaces!
p doc.css('g[groupmode="layer"]').map{ |g| g['id'] }
#=> ["layer1", "g2818"]

How to select XML element by xlink:href attribute with CSS?

Using CSS attribute selectors, you'd need to escape the colon : by a leading backslash\, as follows:

description[xlink\:href="http://book.com/images/HPotter.gif"] {
background-color: gold;
}
<?xml version="1.0" encoding="UTF-8"?>

<bookstore xmlns:xlink="http://www.w3.org/1999/xlink">
<book title="Harry Potter">
<description
xlink:type="simple"
xlink:href="http://book.com/images/HPotter.gif"
xlink:show="new"> ... </description>
</book>

<book title="XQuery Kick Start">
<description
xlink:type="simple"
xlink:href="http://book.com/images/XQuery.gif"
xlink:show="new"> ... </description>
</book>
</bookstore>

WORKING DEMO.



Related Topics



Leave a reply



Submit