How/why is “ *[attribute^="string" ” a valid querySelector? (JS bug?)

The reason why most browsers accept the missing closing bracket is that they're all following the same algorithm from CSS specification. There is a discussion about this on the Chromium bugtracker; here's the relevant excerpt from the comment that closed the bug as WontFix:

When you parse the string 'a[b=c' (from the example link), the CSS parser turns that into:

IDENT(A)
SIMPLE [ BLOCK:
    IDENT(B)
    DELIM(=)
    IDENT(C)

The fact that the square-bracket block was unclosed is lost. You can think of this as the parser "auto-closing all unclosed blocks". If you're using a spec-compliant parser, it is literally impossible to detect an unclosed block unless you do a separate ad hoc parsing on your own just for that purpose.

(I know I'm gravedigging an old question here, but since this still comes up from time to time, the link and explanation may be useful.)


This is primarely opinion-based and is nowhere close to a definitive answer.

Browsers are EXTREMELY complex. Done! Nothing is predictable on them.

First, let's analyze a list of faulty selectors:

  • a[onclick^="ajaxLoad(" (missing ])
  • a[onclick^="ajaxLoad(]" (missing ])
  • a[onclick="" (missing ])
  • a[onclick="][onclick (missing "] or missing " and ] based on what you need)
  • a[onclick=""][onclick (missing ])
  • a[onclick=" (missing "])
  • a[onclick (missing ])
  • a:not([onclick] (missing ))
  • a:not([onclick (missing ]))
  • a:not([onclick=" (missing "]))
  • a:nth-child(5):not([onclick=" (missing "]))
  • a:-webkit-any(:not([onclick=" (missing "])))

So far, this is the list found. I can confirm that these work on Google Chrome 41.0.2272.89m on Windows 7.

Notice the pattern? It's simple: Chrome still can use there selectors to match the elements by filling with basic missing characters, but only at the end! What is missing is so predictable it doesn't require too much effort to fix. But not every selector can/will be 'fixed' (E.g.: a,, can be fixed by adding *).

This may be a bug or a feature (aka, an embarrassing bug submitted as a feature) to soften the eagerness of the CSS engine. This also affects jQuery since jQuery only uses Sizzle if document.querySelectorAll() doesn't exist or throws an exception.

With some time we can find many more.

Disclaimer:

This behaviour shouldn't be relied upon and may change in the future.
This is all based on invalid selectors and invalid syntax (like some IE CSS Hacks for older versions). ALL the working selectors on the list above are against the spec.

The 'unfixed' selector given as an example (a,) works in jQuery, but that is unrelated to this question. Essentially, jQuery will execute it as a.