jsdom は、Node.js で使用するための多くの Web 標準、特に WHATWG DOM および HTML 標準の純粋な JavaScript 実装です。一般に、プロジェクトの目標は、現実世界の 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>
、 <head>
、および<body>
タグを含む、渡された HTML を解析することに注意してください。)
結果として得られるオブジェクトはJSDOM
クラスのインスタンスであり、 window
以外にも多くの便利なプロパティとメソッドが含まれています。一般に、これを使用して「外部」から jsdom に作用し、通常の DOM API では不可能なことを実行できます。この機能が必要ない単純なケースでは、次のようなコーディング パターンをお勧めします。
const { window } = new JSDOM(`...`);// または、evenconst { document } = (new JSDOM(`...`)).window;
JSDOM
クラスで実行できることすべてに関する完全なドキュメントは、以下の「 JSDOM
オブジェクト API」セクションにあります。
JSDOM
コンストラクターは、次の方法で jsdom をカスタマイズするために使用できる 2 番目のパラメーターを受け入れます。
const dom = new JSDOM(``, { URL: "https://example.org/", リファラー: "https://example.com/", contentType: "テキスト/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 コードをフィードする場合にのみこれを使用することを強調します。ユーザーが提供した任意のコード、またはインターネットからのコードでこれを使用すると、事実上、信頼できない Node.js コードを実行することになり、マシンが危険にさらされる可能性があります。
<script src="">
を介して組み込まれた外部スクリプトを実行する場合は、それらがロードされていることを確認する必要もあります。これを行うには、以下で説明するように、オプションresources: "usable"
を追加します。 (ここで説明した理由により、 url
オプションも設定する必要があるでしょう。)
<div onclick="">
などのイベント ハンドラー属性もこの設定によって制御されます。 runScripts
が"dangerously"
に設定されていない限り、それらは機能しません。 (ただし、 div.onclick = ...
などのイベント ハンドラープロパティは、 runScripts
に関係なく機能します。)
<script>
要素とイベント ハンドラー属性を「内部から」実行させるのではなく、単に「外部から」スクリプトを実行しようとしている場合は、 runScripts: "outside-only"
オプションを使用できます。これにより、新しいコピーが有効になります。 JavaScript 仕様で提供されるすべてのグローバルがwindow
にインストールされます。これには、 window.Array
、 window.Promise
などが含まれます。 また、特に、 jsdom window
グローバルとして使用してスクリプトを実行できるwindow.eval
も含まれます。
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 にアクセスする必要があるすべてのスクリプトとテストを実行する必要があります。これには、たとえば、ブラウザで行うのと同じように、 <script>
要素として実行するbrowserify バンドルを作成することが必要になる場合があります。
最後に、高度な使用例では、以下に説明するdom.getInternalVMContext()
メソッドを使用できます。
jsdom にはビジュアル コンテンツをレンダリングする機能がなく、デフォルトではヘッドレス ブラウザのように動作します。 document.hidden
などの API を介して Web ページにコンテンツが表示されないことを示すヒントを提供します。
pretendToBeVisual
オプションがtrue
に設定されている場合、jsdom はコンテンツをレンダリングおよび表示しているふりをします。これは次のようにして行われます。
document.hidden
true
ではなくfalse
返すように変更します。
document.visibilityState
"prerender"
ではなく"visible"
返すように変更します。
存在しないwindow.requestAnimationFrame()
とwindow.cancelAnimationFrame()
メソッドを有効にする
const window = (new JSDOM(``, {pleaseToBeVisual: 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", strictSSL: false、 userAgent: "Mellblomenator/9000",});const dom = new JSDOM(``, { resource: resourceLoader });
ResourceLoader
コンストラクターの 3 つのオプションは次のとおりです。
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 に対して提供された応答をオーバーライドするバージョンを次に示します。
class CustomResourceLoader extends 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
オブジェクトの Promise を返すか、リソースが意図的にロードされない場合はnull
を返す必要があります。一般に、示されているように、ほとんどの場合はsuper.fetch()
に委譲する必要があります。
fetch()
で受け取るオプションの 1 つは、リソースをフェッチする要素 (該当する場合) になります。
class CustomResourceLoader extends 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 自体からのエラーを報告します。これは、 console.error
によって開始されなかった場合でも、Web ブラウザーのコンソールにエラー メッセージがよく表示されるのと似ています。これまでのところ、次のエラーがこの方法で出力されます。
サブリソース (スクリプト、スタイルシート、フレーム、iframe) の読み込みまたは解析時のエラー
true
を返すか、 event.preventDefault()
を呼び出すウィンドウのonerror
イベント ハンドラーによって処理されないスクリプト実行エラー
window.alert
などのメソッドの呼び出しによって生じる未実装エラー。jsdom は実装していませんが、Web 互換性のためにインストールされます。
sendTo(c)
使用してc
にエラーを送信している場合、デフォルトでは、 "jsdomError"
イベントからの情報を使用してc.error(errorStack[, errorDetail])
を呼び出します。イベントとメソッド呼び出しの厳密な 1 対 1 のマッピングを維持し、おそらく"jsdomError"
を自分で処理したい場合は、次のようにします。
virtualConsole.sendTo(c, { submitJSDOMErrors: 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 に特定の値を設定したい場合に主に役立ちます。
クッキージャーはタフクッキーパッケージで提供されます。 jsdom.CookieJar
コンストラクターは、tough-cookie cookie jar のサブクラスであり、デフォルトで、 looseMode: true
オプションを設定します。これは、ブラウザーの動作によりよく一致するためです。 Tough-Cookie のユーティリティとクラスを自分で使用したい場合は、 jsdom.toughCookie
モジュール エクスポートを使用して、jsdom にパッケージ化された Tough-Cookie モジュール インスタンスにアクセスできます。
jsdom を使用すると、非常に早い段階で、つまりWindow
とDocument
オブジェクトが作成された後、HTML が解析されてドキュメントにノードが追加される前に、jsdom の作成に介入できます。
const dom = new JSDOM(`<p>こんにちは</p>`, { beforeParse(window) {window.document.childNodes.length === 0;window.someCoolAPI = () => { /* ... */ }; }});
これは、jsdom がサポートしていない Web プラットフォーム API 用の shim を追加するなど、何らかの方法で環境を変更したい場合に特に便利です。
JSDOM
オブジェクトAPI JSDOM
オブジェクトを構築すると、次の便利な機能が得られます。
プロパティwindow
作成されたWindow
オブジェクトを取得します。
virtualConsole
およびcookieJar
プロパティは、渡したオプション、またはそれらのオプションに何も渡されなかった場合に作成されたデフォルトを反映します。
serialize()
を使用してドキュメントをシリアル化するserialize()
メソッドは、doctype を含むドキュメントの 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)); // null;それは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)); // { startOffset: 13, endOffset: 32 }
この機能は、 includeNodeLocations
オプションを設定した場合にのみ機能することに注意してください。パフォーマンス上の理由から、ノードの場所はデフォルトではオフになっています。
getInternalVMContext()
を使用した Node.js vm
モジュールとのインターフェースNode.js の組み込みvm
モジュールは、jsdom のスクリプト実行魔法を支えています。スクリプトを事前にコンパイルしてから複数回実行するなど、一部の高度な使用例では、 vm
モジュールを jsdom で作成されたWindow
で直接使用することでメリットが得られます。
vm
API での使用に適したコンテキスト化されたグローバル オブジェクトにアクセスするには、 getInternalVMContext()
メソッドを使用できます。
const { スクリプト } = require("vm");const dom = new JSDOM(``, { runScripts: "outside-only" });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")
など) を使用することをお勧めします。
runScripts
を設定せずにJSDOM
インスタンスが作成された場合、または Web ブラウザで jsdom を使用している場合、このメソッドは例外をスローすることに注意してください。
reconfigure(settings)
を使用して jsdom を再構成するwindow
のtop
プロパティは仕様で[Unforgeable]
とマークされています。これは、それが構成不可能な独自のプロパティであるため、 Object.defineProperty
を使用しても、jsdom 内で実行される通常のコードによってオーバーライドまたはシャドウすることができないことを意味します。
同様に、現時点では 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 を構築するための Promise を返すファクトリ メソッドを提供します。
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 はファイル名から jsdom を構築するためのfromFile()
ファクトリ メソッドも提供します。
JSDOM.fromFile("stuff.html", options).then(dom => { console.log(dom.serialize());});
指定されたファイルを開くことができる場合、返された Promise はJSDOM
インスタンスで実行されます。 Node.js API の通常どおり、ファイル名は現在の作業ディレクトリを基準にして指定されます。
fromFile()
に提供されるオプションは、 JSDOM
コンストラクターに提供されるオプションと似ていますが、次の追加のデフォルトがあります。
url
オプションのデフォルトは、 "about:blank"
ではなく、指定されたファイル名に対応するファイル URL になります。
指定されたファイル名が.xht
、 .xhtml
、または.xml
で終わる場合、 contentType
オプションはデフォルトで"application/xhtml+xml"
になります。それ以外の場合は、引き続きデフォルトの"text/html"
が使用されます。
fragment()
最も単純なケースでは、 JSDOM
インスタンス全体とそれに関連するすべての機能が必要ない場合があります。 Window
やDocument
さえ必要ないかもしれません。代わりに、HTML を解析し、操作できる DOM オブジェクトを取得するだけです。そのために、指定された文字列からDocumentFragment
作成するfragment()
があります。
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>
要素を Canvas API で拡張するためのサポートが含まれています。これを機能させるには、プロジェクトの依存関係としてcanvas
jsdom
のピアとして含める必要があります。 jsdom がcanvas
パッケージを見つけることができればそれを使用しますが、それが存在しない場合、 <canvas>
要素は<div>
のように動作します。 jsdom v13 以降、 canvas
のバージョン 2.x が必要です。バージョン 1.x はサポートされなくなりました。
JSDOM
コンストラクターには、文字列を指定するだけでなく、Node.js Buffer
またはArrayBuffer
、 Uint8Array
、 DataView
などの標準 JavaScript バイナリ データ型の形式でバイナリ データを指定することもできます。これが完了すると、jsdom がスニッフィングします。指定されたバイトからエンコードし、ブラウザと同じように<meta charset>
タグをスキャンします。
指定されたcontentType
オプションにcharset
パラメーターが含まれている場合、UTF-8 または UTF-16 BOM が存在する場合を除き、そのエンコーディングがスニッフィングされたエンコーディングをオーバーライドします。その場合、それらが優先されます。 (繰り返しますが、これはブラウザと同じです。)
このエンコード スニッフィングはJSDOM.fromFile()
およびJSDOM.fromURL()
にも適用されます。後者の場合、コンストラクターのcontentType
オプションと同じように、応答とともに送信されたContent-Type
ヘッダーが優先されます。
多くの場合、この方法でバイトを指定する方が、文字列を指定するよりも優れている場合があることに注意してください。たとえば、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 構造を検査するのに適した時期がいつなのかを知ることができません。これは基本的な制限です。 Web ページ上のスクリプトがどのような動作をするかを予測できないため、追加のスクリプトの読み込みがいつ完了するかを知ることはできません。
これはいくつかの方法で回避できます。問題のページを制御している場合の最善の方法は、スクリプト ローダーによって提供されるメカニズムを使用して、読み込みの完了を検出することです。たとえば、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 の範囲外にある 2 つの主要な機能があります。これらは:
ナビゲーション: リンクをクリックするか、 location.href
などを割り当てるときに、グローバル オブジェクトおよびその他すべてのオブジェクトを変更する機能。
Layout : CSS の結果として要素が視覚的に配置される場所を計算する機能。これはgetBoundingClientRects()
などのメソッドやoffsetTop
などのプロパティに影響します。
現在、jsdom には、ナビゲーションのために「未実装」 "jsdomError"
仮想コンソールに送信したり、多くのレイアウト関連プロパティに対してゼロを返したりするなど、これらの機能の一部の側面に対してダミーの動作があります。多くの場合、コード内でこれらの制限を回避できます。たとえば、クロール中に「移動」するページごとに新しいJSDOM
インスタンスを作成したり、 Object.defineProperty()
使用してレイアウト関連のさまざまなゲッターやメソッドが返す内容を変更したりします。
PhantomJS など、同じスペース内の他のツールもこれらの機能をサポートしていることに注意してください。 wiki には、jsdom と PhantomJS についてのより完全な記事があります。
jsdom は、ボランティアのチームによって維持されるコミュニティ主導のプロジェクトです。次の方法で jsdom をサポートできます。
Tidelift サブスクリプションの一環として、jsdom の専門的なサポートを受けることができます。 Tidelift は、チームにメンテナンス、ライセンス、セキュリティの保証を提供しながら、オープンソースを持続可能なものにするのに役立ちます。
プロジェクトに直接貢献します。
jsdomに関するサポートが必要な場合は、以下の会場をご利用ください。
メーリング リスト (「どうすればよいか」の質問に最適)
問題追跡ツール (バグレポートに最適)
マトリックス ルーム: #jsdom:matrix.org