How to Properly Escape Attribute Values in CSS/Js Attribute Selector [Attr=Value]

How to properly escape attribute values in css/js attribute selector [attr=value]?

Yes, that is one correct approach. The Selectors Level 3 specification states the following:

Attribute values must be CSS identifiers or strings.

The example in your question uses a string as the attribute value. An "identifier" is defined as follows:

In CSS, identifiers... can contain only the characters [a-zA-Z0-9] and ISO 10646 characters U+00A0 and higher, plus the hyphen (-) and the underscore (_); they cannot start with a digit, two hyphens, or a hyphen followed by a digit. Identifiers can also contain escaped characters and any ISO 10646 character as a numeric code...

So following that, it is also legal to escape the special characters and omit the quotes:

document.querySelector('input[name=test\\[33\\]]')

CSS attribute selectors: how to escape newlines within attribute value?

The reason why the selector that you have doesn't work:

a[href="\
http://google.com\
"]

Is because:

First, inside a string, a backslash followed by a newline is ignored (i.e., the string is deemed not to contain either the backslash or the newline). Outside a string, a backslash followed by a newline stands for itself (i.e., a DELIM followed by a newline).

This is mentioned in the paragraph above the one you quote from section 4.1.3, and why it says that newlines are one of the characters that cannot be escaped with a backslash.

This makes it equivalent to the following selector:

a[href="    http://google.com"]

Which would only match your element if its attribute value did not contain any newlines.

That said, it is in fact possible to match an element by an attribute value containing newlines. However, CSS makes this a little complicated:

  1. To represent a newline in a CSS string, you need to use the \a escape sequence (case insensitive, up to 6 hex digits). This is stated in section 4.3.7, on strings (which are treated the same whether in a property value or an attribute selector):

    A string cannot directly contain a newline. To include a newline in a string, use an escape representing the line feed character in ISO-10646 (U+000A), such as "\A" or "\00000a". This character represents the generic notion of "newline" in CSS.

    \n has no special meaning in CSS; in fact, it's the same as n.

  2. If a space directly follows an escape sequence such as \a, that space must be doubled so that the space character directly following the escape sequence can be consumed. Refer to section 4.1.3 again, which states:

    Only one white space character is ignored after a hexadecimal escape. Note that this means that a "real" space after the escape sequence must be doubled.

    This may be why you couldn't get it to work even with \a. Understandably, it's an incredibly obscure rule, especially when working with whitespace characters.

This results in the following selector, with the styles applying correctly:

a[href="\a     http://google.com\a"]

Notice that there are five space characters between the first \a and the URL, whereas in comparison there are only four spaces following the newline in your HTML.

Do values in CSS attribute selector values need to be quoted?

TLDR: Quotes are required unless the value meets the identifier specification for CSS2.1

The CSS spec might say they are optional, but the real world presents a different story. When making a comparison against the href attribute you will need to use quotes (single or double work in my very limited testing - latest versions of FF, IE, Chrome.)

Interestingly enough the css spec link referenced by @Pekka happens to use quotes around their href-specific examples.

And it's not just due to non-alpha characters like the period or slashes that give this unique situation a quote requirement - using a partial match selector ~= doesn't work if you just use the "domain" in "domain.com"

Ok, every answer here is wrong (including my own previous answer.) The CSS2 spec didn't clarify whether quotes are required in the selector section itself, but the CSS3 spec does and quotes the rule as a CSS21 implementation:

http://www.w3.org/TR/css3-selectors/

Attribute values must be CSS identifiers or strings. [CSS21] The case-sensitivity of attribute names and values in selectors depends on the document language.

And here is the identifier info:

http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier

In CSS, identifiers (including element names, classes, and IDs in selectors) can contain only the characters [a-zA-Z0-9] and ISO 10646 characters U+00A0 and higher, plus the hyphen (-) and the underscore (_); they cannot start with a digit, two hyphens, or a hyphen followed by a digit. Identifiers can also contain escaped characters and any ISO 10646 character as a numeric code (see next item). For instance, the identifier "B&W?" may be written as "B\&W\?" or "B\26 W\3F".

My answer seemed correct but that's because the '~=' is a white-space selector comparator so it will never match a partial string inside an href value. A '*=' comparator does work however. And a partial string like 'domain' does work for matching href='www.domain.com'. But checking for a full domain name would not work because it violates the identifier rule.

CSS attribute selector for CSS height style

In spec-compliant browsers, tr[style=height:64px] throws a DOM error, saying this is an invalid selector. The version with double quotes works. This is because the spec says that the value in an attribute selector must be a CSS identifier or a string. height:64px is not an identifier, so it fails. Enclosing it in quotation marks (either single or double) makes it into a string, which works.

If you don't want to quote it, then you need to escape the colon using the escape mechanism for CSS identifiers. That is why [style=height\:64px] works (second example below). For details, see this question, of which this question is essentially a duplicate.

[style="height\:64px"] works because escaping the colon is essentially a no-op.

[style="height\3a 64px"] works as expected, with or without surrounding quotes. \3a is the CSS escape for colon, and the following space terminates the escape sequence. I don't know why this didn't work for you. If you were specifying this selector as a JavaScript string, perhaps you forgot to escape the backslash.

[style="height:64px"] does not work because character references only have meaning in HTML. They don't have any meaning in CSS selectors.

None of this has anything to do with whether the style attribute value ends in a semicolon or not. So the description provided by the accepted answer is wrong. It only works because of the quotes.

Bottom line: just wrap your attribute values in quotes and stop worrying about it.

function test(sel) {  try {    console.log(sel, "yields", document.querySelector(sel));  } catch (e) {    console.log(sel, "fails with error", e.name);  }}
test('tr[style=height:64px]');test('tr[style=height\\:64px]');test('tr[style="height:64px"]');test('tr[style="height\\:64px"]');test('tr[style="height\\3a 64px"]'); test('tr[style=height\\3a 64px]');test('tr[style="height:64px"]');
<table>  <tr style="height:64px"></tr></table>

Escaping characters in CSS class selectors

You are getting confused between the class name as present in the DOM, and the escaped version required in CSS. The class name is just specified as is:

elt.classList.add('foo:bar')

It is when this is referenced in a CSS file (or other CSS selector context, such as querySelector) that it must be escaped:

.foo\:bar { color: red; }

The same applies to the case where you choose to use a numeric escape:

elt.classList.add('1234');

.\31 234 { color: red; }

However, no amount of escaping will permit you to have spaces in class names. Spaces are absolutely reserved for delimiting multiple class names on the DOM className attribute.

Is it possible to select all elements with an attribute value greater than a certain number?

No, there's no way in pure CSS.

Possible attribute selectors are:

  • [att]
  • [att=val]
  • [att~=val]
  • [att|=val]

And W3's docs on Attribute Selector adds:

Attribute values must be CSS identifiers or strings. [CSS21] The case-sensitivity of attribute names and values in selectors depends on the document language.

So, they're not numeric. There's no way to use any numeric comparision.

jQuery selector value escaping

I don't think you can. It should be:

#SomeDropdown >option[value='a\'b]<p>']

And this does work as a CSS selector (in modern browsers). Expressed in a JavaScript string literal you would naturally need another round of escaping:

$("#SomeDropdown >option[value='a\\'b]<p>']")

But this doesn't work in jQuery because its selector parser is not completely standards-compliant. It uses this regex to parse the value part of an [attr=value] condition:

(['"]*)(.*?)\3|)\s*\]

\3 being the group containing the opening quotes, which weirdly are allowed to be multiple opening quotes, or no opening quotes at all. The .*? then can parse any character, including quotes until it hits the first ‘]’ character, ending the match. There is no provision for backslash-escaping CSS special characters, so you can't match an arbitrary string value in jQuery.

(Once again, regex parsers lose.)

But the good news is you don't have to rely on jQuery selectors; there are perfectly good DOM methods you can use, in particular HTMLSelectElement.options:

var select= document.getElementById('SomeDropdown');
for (var i= select.options.length; i-->0;) {
if (select.options[i].value=="a'b]<p>") {
// do something with option
} }

This is many times simpler and faster than asking jQuery to laboriously parse and implement your selector, and you can use any value string you like without having to worry about escaping special characters.

CSS selector case insensitive for attributes

It now exists in CSS4, see this answer.

Otherwise, for jQuery, you can use...

$(':input[name]').filter(function() {
return this.value.toLowerCase() == 'search';
});

jsFiddle.

You could also make a custom selector...

$.expr[':'].valueCaseInsensitive = function(node, stackIndex, properties){
return node.value.toLowerCase() == properties[3];
};

var searchInputs = $(':input:valueCaseInsensitive("Search")');

jsFiddle.

The custom selector is a bit of overkill if doing this once, but if you need to use it many times in your application, it may be a good idea.

Update

Is it possible to have that kind of custom selector for any attribute?

Sure, check out the following example. It's a little convoluted (syntax such as :input[value:toLowerCase="search"] may have been more intuitive), but it works :)

$.expr[':'].attrCaseInsensitive = function(node, stackIndex, properties){
var args = properties[3].split(',').map(function(arg) {
return arg.replace(/^\s*["']|["']\s*$/g, '');
});
return $(node).attr(args[0]).toLowerCase() == args[1];
};

var searchInputs = $('input:attrCaseInsensitive(value, "search")');

jsFiddle.

You could probably use eval() to make that string an array, but I find doing it this way more comfortable (and you won't accidentally execute any code you place in your selector).

Instead, I am splitting the string on , delimiter, and then stripping whitespace, ' and " either side of each array member. Note that a , inside a quote won't be treated literally. There is no reason one should be required literally, but you could always code against this possibility. I'll leave that up to you. :)

I don't think map() has the best browser support, so you can explictly iterate over the args array or augment the Array object.

Need Css Selector with attribute containing special escape (apostrophe) character

It seems to work.

$('#mylist li[data-abrv="W\'A"]').css({color:'red'});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script><ul id="mylist">    <li data-abrv="N">None</li>    <li data-abrv="W'A">With'Apos</li></ul>

What chars needs escaping in querySelector?

Updated answer:

In a comment below you said:

The thing is Im making a firefox addon Im doing is identifying items by the attribute in label (cuz class is same for all items). and so querySelector('[label="blah blah blah"]') and the user can edit the label, so thats where the problem comes in, users can make it anything.

Ah, that changes everything. A completely different set of rules applies for the operand in an attribute selector. As you're using " to surround the operand, I think you just have to escape the " with a backslash (and of course, escape any backslashes with a backslash), e.g. the selector (not the string for querySelector, we'll come back to that) for a label with the text testing "one" two three would be [label="testing \"one\" two three"]. So starting with a variable containing the target label, we'd replace all " characters with \" and all \ characters with \\:

var div = document.querySelector('[label="' + theLabel.replace(/["\\]/g, '\\$&') + '"]');

Full example: Live Copy

<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Escaping attributes</title>
</head>
<body>
<div label='testing "one" \two three'>This should turn green</div>
<script>
(function() {
var label = 'testing "one" \\two three';
var div = document.querySelector('[label="' + label.replace(/["\\]/g, '\\$&') + '"]');
div.style.color = "green";
})();
</script>
</body>
</html>

Original answer:

Full details in the CSS specification; for instance, the details for what characters in ID and class selectors need escaping is here:

In CSS, identifiers (including element names, classes, and IDs in selectors) can contain only the characters [a-zA-Z0-9] and ISO 10646 characters U+00A0 and higher, plus the hyphen (-) and the underscore (_); they cannot start with a digit, two hyphens, or a hyphen followed by a digit. Identifiers can also contain escaped characters and any ISO 10646 character as a numeric code (see next item). For instance, the identifier "B&W?" may be written as "B\&W\?" or "B\26 W\3F".

Now, the thing about querySelector / querySelectorAll is that since they take a string, and backslashes are special in string literals, you have to use two backslashes in a string literal to have one backslash in your CSS selector. So for the last example in the quote, suppose you wanted to use that as a class selector. You'd have to do this:

var list = document.querySelectorAll(".B\\26 W\\3F");

...which passes the selector .B\26 W\3F into the selector engine. Live Example | Live Source



Related Topics



Leave a reply



Submit