0

In Blink bug 1045868 I reference that querySelectorAll isn't working with periods. The response I received was:

This is per spec. Selectors containing dots in identifiers need to be escaped, otherwise they are considered starts of class selectors.

Different rendering engines handle this different so there is definitely non-compliance of some kind; the problem is determining who is non-compliant so the issue can be properly addressed.

Where is the initial definition of querySelectorAll and/or where is it specified? Or if I'm dealing with one or more lazy developers where is this being implied from?

TylerH
  • 20,799
  • 66
  • 75
  • 101
John
  • 1
  • 13
  • 98
  • 177

3 Answers3

1

Selectors Level 3: 4. Selector syntax

Characters in Selectors can be escaped with a backslash according to the same escaping rules as CSS. [CSS21].

CSS21: 4.1.3 Characters and case

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

The . (Full Stop) is U+002E so not an allowed character as part of the identifier and has to be escaped due to that.

The . in #music_playlist-2.7 would indicate a selector of this kind #idIdentifer.classIdentifer but .7 is not a valid class identifier as identifiers cannot start with a digit, so depending on how the parser for the selector is implemented it might just ignore that error in the selector, and count .7 as part of the previous identifier, but that's not correct, at least I can't find any part about that in the specs that would justify this:

Selectors Level 3:4. Selector syntax

A selector is a chain of one or more sequences of simple selectors separated by combinators. One pseudo-element may be appended to the last sequence of simple selectors in a selector.

A sequence of simple selectors is a chain of simple selectors that are not separated by a combinator. It always begins with a type selector or a universal selector. No other type selector or universal selector is allowed in the sequence.

A simple selector is either a type selector, universal selector, attribute selector, class selector, ID selector, or pseudo-class.

The universal selector, written as a CSS qualified name [CSS3NAMESPACE] with an asterisk (* U+002A) as the local name, [...] If a universal selector represented by * [...] is not the only component of a sequence of simple selectors selectors or is immediately followed by a pseudo-element, then the * may be omitted and the universal selector's presence implied.

So if you want to use the ID music_playlist-2.7 in your querySelectorAll would write it that way:

document.querySelectorAll('#music_playlist-2\\.7 input[name="payment_form_function"]');

Or if you want to use it in a css rule:

#music_playlist-2\.7 input[name="payment_form_function"] {
  color: red;
}
Community
  • 1
  • 1
t.niese
  • 39,256
  • 9
  • 74
  • 101
0

The alternative selector would be the attribute brackets:

const lazyDevs = document.querySelectorAll('[class="lazy.dev"]');

The following demo shows the above statement working as well as a way to fix the problem in the DOM (there's still CSS and JavaScript to fix).


Demo

/*
The attribute selector works: 
'[class="lazy.dev"]'
*/
const lazyDevsA = document.querySelectorAll('[class="lazy.dev"]');
console.log(`'[class="lazy.dev"]' ~~~ ${lazyDevsA.length} ~~~~~~~~~~~ `);
console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');

/*
In order to find all of the malformed class selectors:
'[class*="."]'
*/
const lazyDevsB = document.querySelectorAll('[class*="."]');
console.log(`'[class*="."]' ~~~~~~~~~ ${lazyDevsB.length} ~~~~~~~~~~~ `);
console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');


/*
- This function will replace the first string parameter
  (rem) with the second string parameter (add)  
- Then replaces the offending className with the new
  className on every tag within the NodeList
*/
function lazyDevKiller(rem, add) {
  const NodeList = document.querySelectorAll('[class*="'+rem+'"]');
  NodeList.forEach(node => {
    let selector = node.className;
    let classAtt = selector.split(rem).join(add);
    node.className = "";
    node.className = classAtt;
  });
}

/*
- This will find all tags that have one or more dots:
  "." (periods or full stops) in their className.
- It will replace each "." with a dash "-" and assign
  the new className to each tag.
- Note: the elements in the result window are labeled:
  "lazy-dev"
*/
lazyDevKiller('.', '-');
aside {
  font: 700 1.25rem/1.5 "Comic Sans MS", cursive, sans-serif;
  color: blue;
  background: goldenrod;
  text-align: center;
  border-bottom: 3px ridge rgba(0, 0, 255, 0.3);
  width: 20vw;
  margin: 20px 10px;
  display: inline-block;
}

aside::after {
  content: ' 'attr(class);
}
<aside class='lazy.dev'></aside>
<aside class='lazy.dev'></aside>
<aside class='lazy.dev'></aside>
<aside class='lazy.dev'></aside>
Community
  • 1
  • 1
zer00ne
  • 41,936
  • 6
  • 41
  • 68
  • And how does this answer the question? – t.niese Apr 15 '20 at 08:58
  • Forgot to mention that it is a problem derived from inexperience or a typo. If one has a good grasp of CSS selectors, one would realize that `.querySelectorAll()` and by default `.querySelector()` are interpreting the strings fine. I offered a working solution to OP's issue at hand. – zer00ne Apr 15 '20 at 09:18
  • The OP has the option that `#music_playlist-2.7 ` is a valid selector and should not throw an error, and opened an issue as of that. And because the issue was closed, is asking where the specs say that it is not valid to have `#music_playlist-2.7` as a selector. `id='music_playlist-2.7'` itself is not invalid, but you need to query it by escaping the `.`. Also for your example you could always do `document.querySelectorAll('.lazy\\.dev')`, or in a css rule `.lazy\.dev { color: red; }` – t.niese Apr 15 '20 at 12:10
-1

If you have for example this element:

<div id='foo.bar'>

You can select it with

document.getElementById('foo.bar')

if you want to select it querySelector you have to escape dot

document.querySelector('#foo\\.bar')
Yukulélé
  • 15,644
  • 10
  • 70
  • 94