我正在使用 NodeJS 和 JSDOM 进行一些抓取。我有:
const fragments = document.querySelectorAll("div[data-qa] a:has(span)");
这将导致消息:
unknown pseudo-class selector ':has(span)'
JSDOM 可以使用
:has()
等伪类吗?
目前在
JSDOM中使用伪类
:has()
有几种方法:
为了实现 DOM Selectors API,JSDOM 使用 NWSAPI,需要更新到版本 >= 2.2.5,因为该版本添加了对
:has()
的基本支持。
但是,目前仍然不支持带有嵌套子选择器的更复杂的表达式
:has()
。
您可以使用polyfill-pseudoclass-has,它不仅可以与浏览器一起使用,还可以与JSDOM一起使用。
这个polyfill很慢,因为它实现了完全符合DOM API(尤其是NodeList),但是它支持使用复杂的表达式(根据规范),例如:
div[data-qa] a:has(span):has(+ strong > span)
。
将 Polyfill 注入 JSDOM 的示例(沙箱):
import { JSDOM } from 'jsdom';
import { addTo } from "polyfill-pseudoclass-has";
const customHtml = `
<h1>Header Example</h1>
<div data-qa>
<p>
<!-- CORRECT -->
<a href="#!1"><span>Example 1</span></a>
<strong><span>Custom text 1!</span></strong>
</p>
<p>
<!-- CORRECT -->
<a href="#!2"><span>Example 2</span></a>
<strong><span>Custom text 2!</span></strong>
</p>
<p>
<!-- INCORRECT: missing <span> inside <strong> -->
<a href="#!3"><span>Example 3</span></a>
<strong>Custom text 3!</strong>
</p>
</div>
`;
const {
window: {document, Element, Document, DocumentFragment},
} = new JSDOM(customHtml);
addTo(Element, Document, DocumentFragment); // inject polyfill
const selector = 'div[data-qa] a:has(span):has(+ strong > span)';
const nodeList = document.querySelectorAll(selector);
console.log(nodeList.length); // => 2
polyfill可以直接使用,无需集成到当前API中:
import { JSDOM } from 'jsdom';
import { SelectorHandler } from "polyfill-pseudoclass-has";
const customHtml = `
...
`;
const { window: {document} } = new JSDOM(customHtml);
const selector = 'div[data-qa] a:has(span):has(+ strong > span)';
const selectorHandler = new SelectorHandler('div[data-qa] a:has(span):has(+ strong > span)');
const nodeList = selectorHandler.queryAll(document);
console.log(nodeList.length); // => 2