我有一个供用户输入 CSS 选择器的字段,我想检查它是否有效(根据 css3 规范)。我尝试按照另一个 stackoverflow 主题中的建议使用 css3 规范中的表达式,但它不起作用 - 我构建的正则表达式与有效的选择器不匹配。我现在所拥有的很简单:
try {
document.querySelector(selector);
} catch (e) {
// handle bad input
}
但这似乎不是一个好的解决方案 - querySelector 函数是为获取元素而设计的,而选择器的检查只是一个副作用。此外,它不提供有关选择器问题的任何信息。
我正在寻找类似
document.validateSelector
或用于解析 CSS 选择器的库。
原始想法的问题是它会搜索整个文档。 慢🤨!
但是,搜索甚至没有附加到 DOM 的空轻量级元素是快✌️!
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')
以下版本稍微先进一点,带有虚拟碎片外壳:
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')
您可以使用库来验证选择器是否有效,并且可能从解析中获得更多详细信息。检查 css 选择器解析器。
感谢@kornieff提示,我已经找到了使用jsdom的nodejs的答案,如果它可以帮助任何人:
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?+"));
由于 querySelector 和 CSS.supports('selector('+s+')') 接受未封闭的输入,如
a[href="foo
,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;
}
不确定它会覆盖所有边缘情况,但在无效选择器上使用
CSS.escape
会返回空NodeList
,而不是抛出语法错误。
document.querySelectorAll(CSS.escape('p > > > a')) => NodeList []
document.querySelectorAll(CSS.escape('a?+')) => NodeList []