检查 CSS 选择器是否有效

问题描述 投票:0回答:5

我有一个供用户输入 CSS 选择器的字段,我想检查它是否有效(根据 css3 规范)。我尝试按照另一个 stackoverflow 主题中的建议使用 css3 规范中的表达式,但它不起作用 - 我构建的正则表达式与有效的选择器不匹配。我现在所拥有的很简单:

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

但这似乎不是一个好的解决方案 - querySelector 函数是为获取元素而设计的,而选择器的检查只是一个副作用。此外,它不提供有关选择器问题的任何信息。

我正在寻找类似

document.validateSelector
或用于解析 CSS 选择器的库。

javascript css-selectors
5个回答
37
投票

原始想法的问题是它会搜索整个文档。 🤨!

但是,搜索甚至没有附加到 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')


1
投票

您可以使用库来验证选择器是否有效,并且可能从解析中获得更多详细信息。检查 css 选择器解析器


1
投票

感谢@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?+"));

1
投票

由于 querySelector 和 CSS.supports('selector('+s+')') 接受未封闭的输入,如

a[href="foo
,
让我们使用内置的 CSSStyleSheet API 来检查选择器是否可以在 CSS 规则中使用:

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;
}

0
投票

不确定它会覆盖所有边缘情况,但在无效选择器上使用

CSS.escape
会返回空
NodeList
,而不是抛出语法错误。

document.querySelectorAll(CSS.escape('p > > > a')) => NodeList []
document.querySelectorAll(CSS.escape('a?+')) => NodeList []
© www.soinside.com 2019 - 2024. All rights reserved.