'Check if CSS selector is valid
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.
Solution 1:[1]
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')
Solution 2:[2]
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?+"));
Solution 3:[3]
Since querySelector and CSS.supports('selector('+s+')') accept unclosed input like a[href="foo,
let's use the built-in CSSStyleSheet API to check the selector can be used in a CSS rule:
let isv;
function isSelectorValid(sel) {
if (!isv) {
try {
// Chrome 73 and newer
isv = new CSSStyleSheet();
} catch (e) {
// This will fail on sites with an unusually strict CSP that forbids inline styles,
// so you'll need to set `nonce` or reuse an existing `link` element.
isv = document.head.appendChild(document.createElement('style')).sheet;
isv.disabled = true;
}
}
let res = false;
try {
// the leading space skips selector's trailing escape char
const body = ` { --foo: "${Math.random()}"; }`;
isv.insertRule(sel + body);
res = isv.cssRules[0].cssText.endsWith(body);
isv.deleteRule(0);
} catch (e) {}
return res;
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Félix |
| Solution 3 | wOxxOm |
