11

I have a field for users to input a CSS selector and I want to check if it's valid (according to css3 specification). I tried to use expressions from css3 specification as suggested in another stackoverflow topic, but it didn't work - the regexp I built just didn't match valid selectors. What I have for now is simply:

try {
     document.querySelector(selector);
} catch (e) {
     // handle bad input
}

But it doesn't seem like a good solution - querySelector function is designed for getting elements, and checking of selector is just a side effect. Furthermore it doesn't provide any information about what is wrong with the selector.

What I'm looking for is something like document.validateSelector or library for parsing CSS selectors.

Community
  • 1
  • 1
Vlad Shevchenko
  • 678
  • 8
  • 19
  • 2
    What's wrong with the way you're doing it? This is the preferred approach as far as I know. –  Jan 18 '16 at 07:13
  • How are users inserting the selector? For example, for a class are the inputing `.className`? Basically, are you prefixing the `selector` with the appropriate identifier for `class` or `id`? – Brett DeWoody Jan 18 '16 at 07:15
  • @Brett DeWoody, yes, they're supposed to type selectors just like in .css files, I don't change it in any way. – Vlad Shevchenko Jan 18 '16 at 07:19
  • 1
    @torazaburo, well, it does work, but it just seams wrong) document.querySelector is not for that. In fact I'm just curious is there a more elegant solution. – Vlad Shevchenko Jan 18 '16 at 07:31
  • It's fine. You're using `try` to pick up an error which is reported by a throw, which is how querySelector reports errors. The only reason to not use this and prefer some other solution is if you wanted details such as the type of error or its character position. –  Jan 18 '16 at 07:37
  • Are you testing if a selector is present on the page? Or if a selector name is a valid selector name, meaning it follows selector name rules? – Brett DeWoody Jan 18 '16 at 07:39
  • Seems quite clear: "check if it's valid". –  Jan 18 '16 at 07:40
  • @BrettDeWoody, I'm testing if it follows selector name rules – Vlad Shevchenko Jan 18 '16 at 07:40
  • This seems to work as expected - https://jsfiddle.net/brettdewoody/hvz9Le7s/. Inserting an invalid selector (starting with a number for example) results in an error. – Brett DeWoody Jan 18 '16 at 07:50
  • @torazaburo: Based on the accepted answer the OP seems to be asking something else entirely - whatever that might be, it's certainly not the question we are looking at. – BoltClock Jan 18 '16 at 10:00
  • @BoltClock The question is simple: how to validate a string with css selector. This string is taken from an input field. OP asks for a better solution, in order to avoid try catch if the selector is not valid. – Dmitri Pavlutin Jan 18 '16 at 10:46
  • Related/duplicate: http://stackoverflow.com/questions/31637782/validation-of-a-css-selector –  Jan 18 '16 at 13:25
  • @DmitriPavlutin I agree this question is unclear. The OP posts code which successfully does something, then asks the question of how to do that. If his question was how to do this **without** `try/catch`, then the question should have stated that, hopefully including a note about why he thinks `try/catch` is so horribly bad. If he has the requirement to detect details or the character location of the error, which could possibly be the case based on the answer he accepted, then that also should have been stated clearly in the question. –  Jan 18 '16 at 13:33
  • @torazaburo OP describes that he found a potential solution, but using a `try` `catch`. It is not a correct approach, because it forces an error generation and use functionality that has a side effect of css validation, but is not actually a validator. He asks for correct ways to accomplish this. I see here everything clear and a good question, especially that author made a good research on the question. – Dmitri Pavlutin Jan 18 '16 at 16:04
  • @DmitriPavlutin The point being discussed here is whether this question is unclear. In its original form, it **was** unclear; in fact, he asked **no question at all**. In any case, it remains unclear why you, or he, thinks the `try/catch` approach is problematic. `throw` is a perfectly standard JS mechanism for reporting errors, and `try/catch` is the perfectly standard JS mechanism for picking them up. Now, that he has edited his question, to ask for a library, then his question is off topic for a different reason, which is that it looking for a library. –  Jan 18 '16 at 16:55
  • @DmitriPavlutin I have no problem with you saying that you need a library to determine exactly **what** is wrong with a selector, but as I mentioned, that aspect was missing from the original question. In the absence of that requirement, there is no basis whatsoever for you asserting that the `try/catch` approach is "incorrect", other than personal preference or some kind of strange allergy to `throw`. –  Jan 18 '16 at 16:56
  • @torazaburo Yes, in the end it is a matter of personal preference. Thanks for your time on discussing this. I would drink with you a beer! Cheers. – Dmitri Pavlutin Jan 18 '16 at 18:57

4 Answers4

19

The problem with original idea is that it will search the entire document. Slow !

However, searching an empty light-weight element that is not even attached to the DOM is fast ✌️!

const queryCheck = s => document.createDocumentFragment().querySelector(s)

const isSelectorValid = selector => {
  try { queryCheck(selector) } catch { return false }
  return true
}

console.assert(isSelectorValid('p > > > a') === false)
console.assert(isSelectorValid('p > a') === true)
console.log('Test passed')

The following version is a bit more advanced with dummy fragment enclosure:

const isSelectorValid = ((dummyElement) =>
  (selector) => {
    try { dummyElement.querySelector(selector) } catch { return false }
    return true
  })(document.createDocumentFragment())
  
console.assert(isSelectorValid('p > > > a') === false)
console.assert(isSelectorValid('p > a') === true)
console.log('Test passed')
kornieff
  • 2,021
  • 15
  • 27
1

You can use a library to verify if the selector is valid, and probably get more details from parsing. Check the css selector parser.

Dmitri Pavlutin
  • 14,964
  • 6
  • 34
  • 37
  • 1
    There's already a library, it's the CSS selector parser built into the browser. That "library" reports errors by throwing. I don't see any reason to bring in another parser. –  Jan 18 '16 at 07:35
  • 2
    Why generate runtime errors, if it is possible to avoid them. A library can give more details on an invalid selector. `document.querySelector(selector)` is designed to query the DOM, but not to check the selector validity. – Dmitri Pavlutin Jan 18 '16 at 10:49
1

Thanks to @kornieff hint, I've reached out to an answer for nodejs using jsdom, if it can help anyone :

const jsdom = require("jsdom");
const { JSDOM } = jsdom;
const { document } = (new JSDOM('')).window;

const queryCheck = s => document.createDocumentFragment().querySelector(s)

const isSelectorValid = selector => {
  try { queryCheck(selector) } catch { return false }
  return true
}
console.log(isSelectorValid("a#x#y"), isSelectorValid("a?+"));
Félix
  • 56
  • 7
-1

In the case the selector is not present, document.querySelector() will return null. In the case the selector is present document.querySelector() will return the element.

Here's a working JSFiddle.

Brett DeWoody
  • 50,328
  • 25
  • 121
  • 168
  • 2
    At least in Chrome, an invalid selector causes a run-time error. Your fiddle generates the error "Uncaught SyntaxError: Failed to execute 'querySelector' on 'Document': '4$$#&*(' is not a valid selector." –  Jan 18 '16 at 07:34
  • No, it throws an error if selector is not valid. Try selector "%^@#" for example. Here is a cite from [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector): Throws a SYNTAX_ERR exception if the specified group of selectors is invalid. – Vlad Shevchenko Jan 18 '16 at 07:37
  • Ah, interesting, so that particular selector throws an error. Could it be because that's not a valid selector name? That is, valid selectors have to start with an underscore, dash, or letter. – Brett DeWoody Jan 18 '16 at 07:38
  • 2
    But that's the whole point--we're trying to check the validity, right? –  Jan 18 '16 at 07:39
  • I misunderstood the question. I read it as testing if the selector was present on the page. – Brett DeWoody Jan 18 '16 at 07:42