jsdom 是许多 Web 标准的纯 JavaScript 实现,特别是 WHATWG DOM 和 HTML 标准,可与 Node.js 一起使用。一般来说,该项目的目标是模拟足够多的 Web 浏览器子集,以用于测试和抓取真实世界的 Web 应用程序。
最新版本的 jsdom 需要 Node.js v18 或更高版本。 (低于 v23 的 jsdom 版本仍然可以与以前的 Node.js 版本一起使用,但不受支持。)
const jsdom = require("jsdom");const { JSDOM } = jsdom;
要使用 jsdom,您将主要使用JSDOM
构造函数,它是 jsdom 主模块的命名导出。向构造函数传递一个字符串。您将得到一个JSDOM
对象,它具有许多有用的属性,特别是window
:
const dom = new JSDOM(`<!DOCTYPE html><p>Hello world</p>`);console.log(dom.window.document.querySelector("p").textContent); // “你好世界”
(请注意,jsdom 将像浏览器一样解析您传递给它的 HTML,包括隐含的<html>
、 <head>
和<body>
标签。)
生成的对象是JSDOM
类的实例,除了window
之外,它还包含许多有用的属性和方法。一般来说,它可以用来从“外部”作用于 jsdom,做普通 DOM API 无法完成的事情。对于不需要任何此功能的简单情况,我们建议使用如下编码模式
const { window } = new JSDOM(`...`);// 或者甚至 const { document } = (new JSDOM(`...`)).window;
有关JSDOM
类可以执行的所有操作的完整文档位于下面的“ JSDOM
对象 API”部分中。
JSDOM
构造函数接受第二个参数,可用于通过以下方式自定义 jsdom。
const dom = new JSDOM(``, { 网址:“https://example.org/”, 引荐来源:“https://example.com/”, 内容类型:“文本/html”, includeNodeLocations:true, 存储配额:10000000});
url
设置window.location
、 document.URL
和document.documentURI
返回的值,并影响文档中相对 URL 的解析以及获取子资源时使用的同源限制和引荐来源网址等内容。它默认为"about:blank"
。
referrer
仅影响从document.referrer
读取的值。它默认没有引用者(反映为空字符串)。
contentType
影响从document.contentType
读取的值,以及文档的解析方式:解析为 HTML 或解析为 XML。非 HTML MIME 类型或 XML MIME 类型的值将抛出。它默认为"text/html"
。如果存在charset
参数,它可能会影响二进制数据处理。
includeNodeLocations
保留 HTML 解析器生成的位置信息,允许您使用nodeLocation()
方法检索它(如下所述)。它还确保在<script>
元素内运行的代码的异常堆栈跟踪中报告的行号是正确的。它默认为false
以提供最佳性能,并且不能与 XML 内容类型一起使用,因为我们的 XML 解析器不支持位置信息。
storageQuota
是localStorage
和sessionStorage
使用的单独存储区域的最大大小(以代码单元为单位)。尝试存储大于此限制的数据将导致抛出DOMException
。默认情况下,受 HTML 规范的启发,每个源设置为 5,000,000 个代码单元。
请注意, url
和referrer
在使用之前都会被规范化,因此,例如,如果您传入"https:example.com"
,jsdom 会将其解释为就像您给出了"https://example.com/"
一样。如果您传递无法解析的 URL,调用将会抛出。 (URL 根据 URL 标准进行解析和序列化。)
jsdom最强大的能力是它可以在jsdom内部执行脚本。这些脚本可以修改页面的内容并访问jsdom实现的所有Web平台API。
然而,在处理不受信任的内容时,这也是非常危险的。 jsdom 沙箱并非万无一失,在 DOM 的<script>
内运行的代码如果足够努力,就可以访问 Node.js 环境,从而访问您的计算机。因此,默认情况下禁用执行嵌入在 HTML 中的脚本的功能:
const dom = new JSDOM(`<body> <div id="content"></div> <script>document.getElementById("content").append(document.createElement("hr"));</script> </body>`);// 脚本不会被执行,默认:console.log(dom.window.document.getElementById("content").children.length); // 0
要在页面内执行脚本,您可以使用runScripts: "dangerously"
选项:
const dom = new JSDOM(`<body> <div id="content"></div> <script>document.getElementById("content").append(document.createElement("hr"));</script> </body>`, { runScripts: "dangerously" });// 脚本将被执行并修改 DOM:console.log(dom.window.document.getElementById("content").children.length); // 1
我们再次强调,仅在提供您知道安全的 jsdom 代码时才使用此选项。如果您在任意用户提供的代码或来自 Internet 的代码上使用它,那么您实际上正在运行不受信任的 Node.js 代码,并且您的计算机可能会受到损害。
如果您想执行通过<script src="">
包含的外部脚本,您还需要确保它们加载它们。为此,请添加选项resources: "usable"
,如下所述。 (出于此处讨论的原因,您可能还想设置url
选项。)
事件处理程序属性,例如<div onclick="">
,也受此设置的控制;除非将runScripts
设置为"dangerously"
否则它们将无法运行。 (但是,事件处理程序属性,如div.onclick = ...
,无论runScripts
如何都会起作用。)
如果您只是尝试“从外部”执行脚本,而不是让<script>
元素和事件处理程序属性“从内部”运行,您可以使用runScripts: "outside-only"
选项,该选项可以启用新的脚本副本所有 JavaScript 规范提供的全局变量都安装在window
上。这包括window.Array
、 window.Promise
等。值得注意的是,它还包括window.eval
,它允许运行脚本,但将 jsdom window
作为全局窗口:
const dom = new JSDOM(`<body> <div id="content"></div> <script>document.getElementById("content").append(document.createElement("hr"));</script> </body>`, { runScripts: "outside-only" });// 在外部运行脚本JSDOM:dom.window.eval('document.getElementById("content").append(document.createElement("p"));');console.log(dom.window.document.getElementById("content").儿童.长度); // 1console.log(dom.window.document.getElementsByTagName("hr").length); // 0console.log(dom.window.document.getElementsByTagName("p").length); // 1
出于性能原因,此功能默认关闭,但启用是安全的。
请注意,在默认配置中,如果不设置runScripts
,则window.Array
、 window.eval
等的值将与外部 Node.js 环境提供的值相同。也就是说, window.eval === eval
将成立,因此window.eval
将不会以有用的方式运行脚本。
我们强烈建议不要尝试通过将 jsdom 和 Node 全局环境混合在一起(例如通过执行global.window = dom.window
)来“执行脚本”,然后在 Node 全局环境中执行脚本或测试代码。相反,您应该像对待浏览器一样对待 jsdom,并使用window.eval
或runScripts: "dangerously"
运行需要访问 jsdom 环境内 DOM 的所有脚本和测试。例如,这可能需要创建一个 browserify 包以作为<script>
元素执行 - 就像在浏览器中一样。
最后,对于高级用例,您可以使用dom.getInternalVMContext()
方法,如下所述。
jsdom 没有渲染视觉内容的能力,默认情况下就像无头浏览器一样。它通过document.hidden
等 API 向网页提供其内容不可见的提示。
当pretendToBeVisual
选项设置为true
时,jsdom 将假装它正在渲染和显示内容。它通过以下方式做到这一点:
更改document.hidden
以返回false
而不是true
更改document.visibilityState
以返回"visible"
而不是"prerender"
启用window.requestAnimationFrame()
和window.cancelAnimationFrame()
方法,否则这些方法不存在
const window = (new JSDOM(``, { fakeToBeVisual: true })).window;window.requestAnimationFrame(timestamp => { console.log(时间戳 > 0);});
请注意,jsdom 仍然不进行任何布局或渲染,因此这实际上只是假装可视化,而不是实现真正的可视化 Web 浏览器将实现的平台部分。
默认情况下,jsdom 不会加载任何子资源,例如脚本、样式表、图像或 iframe。如果你想让 jsdom 加载此类资源,你可以传递resources: "usable"
选项,这将加载所有可用的资源。这些是:
框架和 iframe,通过<frame>
和<iframe>
样式表,通过<link rel="stylesheet">
脚本,通过<script>
,但前提是还设置了runScripts: "dangerously"
图像,通过<img>
,但前提是还安装了canvas
npm 包(请参阅下面的“Canvas 支持”)
尝试加载资源时,请记住url
选项的默认值为"about:blank"
,这意味着通过相对 URL 包含的任何资源都将无法加载。 (尝试根据 URL about:blank
解析 URL /something
的结果是错误。)因此,在这些情况下,您可能需要为url
选项设置非默认值,或者使用其中一种方便的方法自动执行此操作的 API。
要更全面地自定义 jsdom 的资源加载行为,您可以传递ResourceLoader
类的实例作为resources
选项值:
const ResourceLoader = new jsdom.ResourceLoader({ 代理:“http://127.0.0.1:9001”, 严格SSL:假, userAgent: "Mellblomenator/9000",});const dom = new JSDOM(``, { resources: resourceLoader });
ResourceLoader
构造函数的三个选项是:
proxy
是要使用的 HTTP 代理的地址。
strictSSL
可以设置为 false 以禁用 SSL 证书有效的要求。
userAgent
影响发送的User-Agent
标头,从而影响navigator.userAgent
的结果值。它默认为`Mozilla/5.0 (${process.platform || "unknown OS"}) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/${jsdomVersion}`
。
您可以通过子类化ResourceLoader
并重写fetch()
方法来进一步自定义资源获取。例如,以下版本会覆盖为特定 URL 提供的响应:
类 CustomResourceLoader 扩展 jsdom.ResourceLoader { fetch(url, options) {// 覆盖此脚本的内容以执行一些不寻常的操作。if (url === "https://example.com/some-specific-script.js") { return Promise.resolve( Buffer.from("window.someGlobal = 5;"));}return super.fetch(url, options); }}
每当 jsdom 遇到“可用”资源时,就会根据上面的部分调用您的自定义资源加载器的fetch()
方法。该方法接受一个 URL 字符串,以及一些选项,如果调用super.fetch()
您应该不加修改地传递这些选项。它必须返回 Node.js Buffer
对象的承诺,或者如果有意不加载资源则返回null
。一般来说,大多数情况下都希望委托给super.fetch()
,如图所示。
您将在fetch()
中收到的选项之一是正在获取资源的元素(如果适用)。
类 CustomResourceLoader 扩展 jsdom.ResourceLoader { fetch(url, options) {if (options.element) { console.log(`Element ${options.element.localName} 正在请求 url ${url}`);}return super.fetch(url, options); }}
与 Web 浏览器一样,jsdom 也有“控制台”的概念。这记录了通过在文档内执行的脚本直接从页面发送的信息,以及来自 jsdom 实现本身的信息。我们将用户可控制的控制台称为“虚拟控制台”,以区别于 Node.js console
API 和页面内部window.console
API。
默认情况下, JSDOM
构造函数将返回一个带有虚拟控制台的实例,该虚拟控制台将其所有输出转发到 Node.js 控制台。要创建您自己的虚拟控制台并将其传递给 jsdom,您可以通过执行以下操作来覆盖此默认值
const virtualConsole = new jsdom.VirtualConsole();const dom = new JSDOM(``, { virtualConsole });
这样的代码将创建一个没有任何行为的虚拟控制台。您可以通过为所有可能的控制台方法添加事件侦听器来赋予它行为:
virtualConsole.on("错误", () => { ... });virtualConsole.on("警告", () => { ... });virtualConsole.on("信息", () => { ... });virtualConsole.on("dir", () => { ... });// ... 等等。请参阅 https://console.spec.whatwg.org/#logging
(请注意,最好在调用new JSDOM()
之前设置这些事件侦听器,因为在解析过程中可能会出现错误或控制台调用脚本。)
如果您只是想将虚拟控制台输出重定向到另一个控制台(例如默认的 Node.js 控制台),您可以这样做
virtualConsole.sendTo(控制台);
还有一个特殊事件"jsdomError"
,它将触发错误对象以报告 jsdom 本身的错误。这类似于错误消息通常在 Web 浏览器控制台中显示的方式,即使它们不是由console.error
启动的。到目前为止,这样输出的错误如下:
加载或解析子资源(脚本、样式表、框架和 iframe)时出错
返回true
或调用event.preventDefault()
窗口onerror
事件处理程序未处理的脚本执行错误
由于调用方法而导致的未实现错误,例如window.alert
,jsdom 未实现该方法,但为了 Web 兼容性而安装
如果您使用sendTo(c)
将错误发送到c
,默认情况下它将使用来自"jsdomError"
事件的信息调用c.error(errorStack[, errorDetail])
。如果您希望维护事件到方法调用的严格一对一映射,并且可能自己处理"jsdomError"
,那么您可以这样做
virtualConsole.sendTo(c, { omitJSDOMErrors: true });
与 Web 浏览器一样,jsdom 有 cookie jar 的概念,用于存储 HTTP cookie。具有与文档相同域的 URL 且未标记为仅 HTTP 的 Cookie 可以通过document.cookie
API 进行访问。此外,cookie jar 中的所有 cookie 都会影响子资源的获取。
默认情况下, JSDOM
构造函数将返回一个带有空 cookie jar 的实例。要创建您自己的 cookie jar 并将其传递给 jsdom,您可以通过执行以下操作来覆盖此默认值
const cookieJar = new jsdom.CookieJar(store, options);const dom = new JSDOM(``, { cookieJar });
如果您想在多个 jsdom 之间共享相同的 cookie jar,或者提前用某些值填充 cookie jar,那么这非常有用。
Cookie 罐子由tough-cookie 包提供。 jsdom.CookieJar
构造函数是tough-cookie cookie jar的子类,默认情况下设置looseMode: true
选项,因为这更好地匹配浏览器的行为方式。如果您想自己使用tough-cookie的实用程序和类,您可以使用jsdom.toughCookie
模块导出来访问与jsdom打包的tough-cookie模块实例。
jsdom 允许您很早就干预 jsdom 的创建:在创建Window
和Document
对象之后,但在解析任何 HTML 以使用节点填充文档之前:
const dom = new JSDOM(`<p>你好</p>`, { beforeParse(window) {window.document.childNodes.length === 0;window.someCoolAPI = () => { /* ... */ }; }});
如果您想以某种方式修改环境,例如为 jsdom 不支持的 Web 平台 API 添加填充程序,这尤其有用。
JSDOM
对象 API一旦构建了JSDOM
对象,它将具有以下有用的功能:
属性window
检索为您创建的Window
对象。
virtualConsole
和cookieJar
属性反映您传入的选项,或者如果没有为这些选项传入任何内容,则反映为您创建的默认值。
serialize()
序列化文档serialize()
方法将返回文档的 HTML 序列化,包括文档类型:
const dom = new JSDOM(`<!DOCTYPE html>hello`);dom.serialize() === "<!DOCTYPE html><html><head></head><body>hello</body></ html>";//对比:dom.window.document.documentElement.outerHTML === "<html><head></head><body>hello</body></html>";
nodeLocation(node)
获取节点的源位置nodeLocation()
方法将查找 DOM 节点在源文档中的位置,并返回该节点的 parse5 位置信息:
const dom = 新的 JSDOM( `<p>你好<img src="foo.jpg"></p>`, { includeNodeLocations: true });const document = dom.window.document;const bodyEl = document.body; // 隐式创建 const pEl = document.querySelector("p");const textNode = pEl.firstChild;const imgEl = document.querySelector("img");console.log(dom.nodeLocation(bodyEl)); // 无效的;它不在 sourceconsole.log(dom.nodeLocation(pEl)); 中 // { startOffset: 0, endOffset: 39, startTag: ..., endTag: ... }console.log(dom.nodeLocation(textNode)); // { startOffset: 3, endOffset: 13 }console.log(dom.nodeLocation(imgEl)); // { 起始偏移量:13,结束偏移量:32 }
请注意,此功能仅在您设置了includeNodeLocations
选项后才有效;出于性能原因,节点位置默认处于关闭状态。
getInternalVMContext()
与 Node.js vm
模块交互Node.js 的内置vm
模块是 jsdom 脚本运行魔力的基础。一些高级用例,例如预编译脚本然后多次运行它,可以受益于直接将vm
模块与 jsdom 创建的Window
一起使用。
要访问适合与vm
API 一起使用的上下文全局对象,您可以使用getInternalVMContext()
方法:
const { Script } = require("vm");const dom = new JSDOM(``, { runScripts: "仅外部" });const script = new Script(` if (!this.ran) { this.ran = 0; } ++this.ran;`);const vmContext = dom.getInternalVMContext();script.runInContext(vmContext);script.runInContext(vmContext);script.runInContext(vmContext);console.assert(dom.window.ran === 3);
这是有点高级的功能,我们建议坚持使用普通的 DOM API(例如window.eval()
或document.createElement("script")
),除非您有非常具体的需求。
请注意,如果JSDOM
实例是在没有设置runScripts
情况下创建的,或者如果您在 Web 浏览器中使用 jsdom,则此方法将引发异常。
reconfigure(settings)
重新配置 jsdom window
的top
属性在规范中被标记为[Unforgeable]
,这意味着它是一个不可配置的自有属性,因此不能被 jsdom 内运行的正常代码覆盖或隐藏,即使使用Object.defineProperty
也是如此。
同样,目前 jsdom 不处理导航(例如设置window.location.href = "https://example.com/"
);这样做会导致虚拟控制台发出"jsdomError"
解释该功能尚未实现,并且不会发生任何变化:不会有新的Window
或Document
对象,并且现有window
的location
对象仍将具有相同的内容属性值。
但是,如果您从窗口外部进行操作,例如在某些创建 jsdom 的测试框架中,您可以使用特殊的reconfigure()
方法覆盖其中一个或两个:
const dom = new JSDOM();dom.window.top === dom.window;dom.window.location.href === "about:blank";dom.reconfigure({ windowTop: myFakeTopForTesting, url: "https: //example.com/" });dom.window.top === myFakeTopForTesting;dom.window.location.href === "https://example.com/";
请注意,更改 jsdom 的 URL 将影响所有返回当前文档 URL 的 API,例如window.location
、 document.URL
和document.documentURI
,以及文档中相对 URL 的解析以及同源检查和获取子资源时使用的引用者。但是,它不会执行对该 URL 内容的导航; DOM 的内容将保持不变,并且不会创建Window
、 Document
等的新实例。
fromURL()
除了JSDOM
构造函数本身之外,jsdom 还提供了一个承诺返回工厂方法,用于从 URL 构造 jsdom:
JSDOM.fromURL("https://example.com/", options).then(dom => { console.log(dom.serialize());});
如果 URL 有效且请求成功,则返回的 Promise 将通过JSDOM
实例实现。任何重定向都将被跟踪到其最终目的地。
提供给fromURL()
选项与提供给JSDOM
构造函数的选项类似,但具有以下附加限制和后果:
无法提供url
和contentType
选项。
referrer
选项用作初始请求的 HTTP Referer
请求标头。
resources
选项也会影响初始请求;例如,如果您想要配置代理(请参见上文),这将很有用。
生成的 jsdom 的 URL、内容类型和引荐来源网址是根据响应确定的。
通过 HTTP Set-Cookie
响应标头设置的任何 cookie 都存储在 jsdom 的 cookie jar 中。同样,所提供的 cookie jar 中已有的任何 cookie 都会作为 HTTP Cookie
请求标头发送。
fromFile()
与fromURL()
类似,jsdom 也提供了fromFile()
工厂方法,用于从文件名构造 jsdom:
JSDOM.fromFile("stuff.html", options).then(dom => { console.log(dom.serialize());});
如果可以打开给定的文件,则返回的承诺将通过JSDOM
实例实现。与 Node.js API 中通常的情况一样,文件名是相对于当前工作目录给出的。
提供给fromFile()
选项与提供给JSDOM
构造函数的选项类似,具有以下附加默认值:
url
选项默认为与给定文件名相对应的文件 URL,而不是"about:blank"
。
如果给定的文件名以.xht
、 .xhtml
或.xml
结尾,则contentType
选项将默认为"application/xhtml+xml"
否则它将继续默认为"text/html"
。
fragment()
对于最简单的情况,您可能不需要一个完整的JSDOM
实例及其所有相关功能。您甚至可能不需要Window
或Document
!相反,您只需要解析一些 HTML,并获取可以操作的 DOM 对象。为此,我们有fragment()
,它根据给定的字符串创建一个DocumentFragment
:
const frag = JSDOM.fragment(`<p>你好</p><p><strong>嗨!</strong>`);frag.childNodes.length === 2;frag.querySelector("strong"). textContent === "嗨!";//等等
这里的frag
是一个DocumentFragment
实例,它的内容是通过解析提供的字符串创建的。解析是使用<template>
元素完成的,因此您可以在其中包含任何元素(包括具有奇怪解析规则的元素,例如<td>
)。同样重要的是要注意,生成的DocumentFragment
将不会有关联的浏览上下文:也就是说,元素的ownerDocument
将具有 null defaultView
属性,资源将不会加载等。
对fragment()
工厂的所有调用都会产生共享相同模板所有者Document
的DocumentFragment
。这允许对fragment()
进行多次调用,而无需额外的开销。但这也意味着对fragment()
调用不能使用任何选项进行自定义。
请注意,使用DocumentFragment
进行序列化并不像使用完整的JSDOM
对象那么容易。如果您需要序列化 DOM,您可能应该更直接地使用JSDOM
构造函数。但对于包含单个元素的片段的特殊情况,通过正常方法很容易做到:
const frag = JSDOM.fragment(`<p>Hello</p>`);console.log(frag.firstChild.outerHTML); // 记录“<p>你好</p>”
jsdom 支持使用canvas
包通过 canvas API 扩展任何<canvas>
元素。要实现此功能,您需要将canvas
作为项目中的依赖项包含进来,作为jsdom
的同级。如果 jsdom 可以找到canvas
包,它将使用它,但如果它不存在,那么<canvas>
元素的行为将类似于<div>
。从jsdom v13开始,需要canvas
2.x版本;不再支持 1.x 版本。
除了提供字符串之外, JSDOM
构造函数还可以以 Node.js Buffer
或标准 JavaScript 二进制数据类型(如ArrayBuffer
、 Uint8Array
、 DataView
等)的形式提供二进制数据。完成此操作后,jsdom 将嗅探来自提供的字节的编码,像浏览器一样扫描<meta charset>
标签。
如果提供的contentType
选项包含charset
参数,则该编码将覆盖嗅探到的编码 - 除非存在 UTF-8 或 UTF-16 BOM,在这种情况下,这些编码优先。 (再次强调,这就像浏览器一样。)
这种编码嗅探也适用于JSDOM.fromFile()
和JSDOM.fromURL()
。在后一种情况下,与响应一起发送的任何Content-Type
标头都将优先,与构造函数的contentType
选项相同。
请注意,在许多情况下,以这种方式提供字节可能比提供字符串更好。例如,如果您尝试使用 Node.js 的buffer.toString("utf-8")
API,Node.js 将不会删除任何前导 BOM。如果您随后将此字符串提供给 jsdom,它将逐字解释它,而 BOM 保持不变。但jsdom的二进制数据解码代码会像浏览器一样剥离前导BOM;在这种情况下,直接提供buffer
将得到所需的结果。
根据定义,jsdom 中的计时器(由window.setTimeout()
或window.setInterval()
设置)将在将来在窗口上下文中执行代码。由于将来无法在不保持进程处于活动状态的情况下执行代码,因此出色的 jsdom 计时器将使 Node.js 进程保持活动状态。类似地,由于无法在不保持对象活动状态的情况下在对象上下文中执行代码,因此未完成的 jsdom 计时器将阻止对其调度的窗口进行垃圾收集。
如果您想确保关闭 jsdom 窗口,请使用window.close()
,这将终止所有正在运行的计时器(并且还会删除窗口和文档上的任何事件侦听器)。
在 Node.js 中,您可以使用 Chrome DevTools 调试程序。请参阅官方文档了解如何开始。
默认情况下,jsdom 元素在控制台中被格式化为普通的旧 JS 对象。为了更容易调试,您可以使用 jsdom-devtools-formatter,它可以让您像检查真实的 DOM 元素一样检查它们。
人们在使用 jsdom 时经常遇到异步脚本加载的问题。许多页面异步加载脚本,但无法判断它们何时完成,因此无法判断何时适合运行代码并检查生成的 DOM 结构。这是一个根本性的限制;我们无法预测网页上的脚本将执行哪些操作,因此无法告诉您它们何时完成加载更多脚本。
这可以通过几种方式解决。如果您控制有问题的页面,最好的方法是使用脚本加载器提供的任何机制来检测加载何时完成。例如,如果您使用像 RequireJS 这样的模块加载器,则代码可能如下所示:
// 在 Node.js 端:const window = (new JSDOM(...)).window;window.onModulesLoaded = () => { console.log("准备好了!");};
<!-- 在您提供给 jsdom 的 HTML 中 --><script>requirejs(["entry-module"], () => { window.onModulesLoaded();});</script>
如果您无法控制页面,则可以尝试解决方法,例如轮询特定元素是否存在。
有关更多详细信息,请参阅 #640 中的讨论,尤其是 @matthewkastor 的富有洞察力的评论。
尽管我们喜欢向 jsdom 添加新功能并使其与最新的 Web 规范保持同步,但它仍然缺少许多 API。请随时针对任何缺失的内容提出问题,但我们是一个小而繁忙的团队,因此拉取请求可能会效果更好。
jsdom 的一些功能是由我们的依赖项提供的。这方面值得注意的文档包括我们的 CSS 选择器引擎nwsapi
支持的 CSS 选择器列表。
除了我们尚未了解的功能之外,还有两个主要功能目前超出了 jsdom 的范围。这些都是:
导航:单击链接或分配location.href
或类似内容时更改全局对象和所有其他对象的能力。
Layout :计算元素在 CSS 中的视觉布局位置的能力,这会影响getBoundingClientRects()
等方法或offsetTop
等属性。
目前 jsdom 对于这些功能的某些方面具有虚拟行为,例如向虚拟控制台发送“未实现” "jsdomError"
以进行导航,或为许多与布局相关的属性返回零。通常,您可以在代码中解决这些限制,例如,通过为爬网期间“导航”到的每个页面创建新的JSDOM
实例,或者使用Object.defineProperty()
更改各种与布局相关的 getter 和方法返回的内容。
请注意,同一领域的其他工具(例如 PhantomJS)确实支持这些功能。在 wiki 上,我们有关于 jsdom 与 PhantomJS 的更完整的文章。
jsdom 是一个由志愿者团队维护的社区驱动项目。您可以通过以下方式支持 jsdom:
作为 Tidelift 订阅的一部分,获得 jsdom 的专业支持。 Tidelift 帮助我们实现开源的可持续发展,同时为团队提供维护、许可和安全方面的保证。
直接为该项目做出贡献。
如果您需要 jsdom 方面的帮助,请随时使用以下任一场所:
邮件列表(最适合“我如何”问题)
问题跟踪器(最适合错误报告)
矩阵房间:#jsdom:matrix.org