jsdom adalah implementasi JavaScript murni dari banyak standar web, terutama DOM WHATWG dan Standar HTML, untuk digunakan dengan Node.js. Secara umum, tujuan proyek ini adalah untuk meniru sebagian dari browser web agar berguna untuk menguji dan menghapus aplikasi web dunia nyata.
Versi terbaru jsdom memerlukan Node.js v18 atau lebih baru. (Versi jsdom di bawah v23 masih berfungsi dengan versi Node.js sebelumnya, namun tidak didukung.)
const jsdom = memerlukan("jsdom");const { JSDOM } = jsdom;
Untuk menggunakan jsdom, Anda terutama akan menggunakan konstruktor JSDOM
, yang merupakan ekspor bernama modul utama jsdom. Berikan string kepada konstruktor. Anda akan mendapatkan kembali objek JSDOM
, yang memiliki sejumlah properti berguna, terutama window
:
const dom = new JSDOM(`<!DOCTYPE html><p>Halo dunia</p>`);console.log(dom.window.document.querySelector("p").textContent); // "Halo dunia"
(Perhatikan bahwa jsdom akan mengurai HTML yang Anda berikan seperti yang dilakukan browser, termasuk tag <html>
, <head>
, dan <body>
yang tersirat.)
Objek yang dihasilkan adalah turunan dari kelas JSDOM
, yang berisi sejumlah properti dan metode berguna selain window
. Secara umum, ini dapat digunakan untuk bertindak di jsdom dari "luar", melakukan hal-hal yang tidak mungkin dilakukan dengan API DOM normal. Untuk kasus sederhana, di mana Anda tidak memerlukan fungsi ini, kami merekomendasikan pola pengkodean seperti
const { window } = new JSDOM(`...`);// atau evenconst { document } = (new JSDOM(`...`)).window;
Dokumentasi lengkap tentang segala hal yang dapat Anda lakukan dengan kelas JSDOM
ada di bawah, di bagian " API Objek JSDOM
".
Konstruktor JSDOM
menerima parameter kedua yang dapat digunakan untuk menyesuaikan jsdom Anda dengan cara berikut.
const dom = JSDOM baru(``, { url: "https://example.org/", perujuk: "https://example.com/", tipe konten: "teks/html", includeNodeLocations: benar, penyimpananKuota: 10000000});
url
menetapkan nilai yang dikembalikan oleh window.location
, document.URL
, dan document.documentURI
, dan memengaruhi hal-hal seperti resolusi URL relatif dalam dokumen serta batasan asal yang sama dan perujuk yang digunakan saat mengambil subsumber daya. Standarnya adalah "about:blank"
.
referrer
hanya memengaruhi nilai yang dibaca dari document.referrer
. Standarnya adalah tidak ada perujuk (yang mencerminkan string kosong).
contentType
memengaruhi nilai yang dibaca dari document.contentType
, serta cara dokumen diurai: sebagai HTML atau XML. Nilai yang bukan tipe MIME HTML atau tipe MIME XML akan ditampilkan. Standarnya adalah "text/html"
. Jika ada parameter charset
, hal ini dapat memengaruhi pemrosesan data biner.
includeNodeLocations
mempertahankan informasi lokasi yang dihasilkan oleh parser HTML, memungkinkan Anda mengambilnya dengan metode nodeLocation()
(dijelaskan di bawah). Hal ini juga memastikan bahwa nomor baris yang dilaporkan dalam jejak tumpukan pengecualian untuk kode yang berjalan di dalam elemen <script>
sudah benar. Defaultnya adalah false
untuk memberikan performa terbaik, dan tidak dapat digunakan dengan tipe konten XML karena parser XML kami tidak mendukung info lokasi.
storageQuota
adalah ukuran maksimum dalam unit kode untuk area penyimpanan terpisah yang digunakan oleh localStorage
dan sessionStorage
. Upaya untuk menyimpan data yang lebih besar dari batas ini akan menyebabkan DOMException
dilempar. Secara default, ini diatur ke 5.000.000 unit kode per asal, sesuai dengan spesifikasi HTML.
Perhatikan bahwa url
dan referrer
dikanonikalisasi sebelum digunakan, jadi misalnya jika Anda meneruskan "https:example.com"
, jsdom akan menafsirkannya seolah-olah Anda telah memberikan "https://example.com/"
. Jika Anda meneruskan URL yang tidak dapat diurai, panggilan akan dibatalkan. (URL diurai dan diserialkan sesuai dengan Standar URL.)
Kemampuan jsdom yang paling kuat adalah dapat mengeksekusi skrip di dalam jsdom. Skrip ini dapat mengubah konten halaman dan mengakses semua API platform web yang diimplementasikan jsdom.
Namun, hal ini juga sangat berbahaya bila berhadapan dengan konten yang tidak tepercaya. Sandbox jsdom tidak mudah digunakan, dan kode yang berjalan di dalam <script>
DOM dapat, jika berusaha cukup keras, mendapatkan akses ke lingkungan Node.js, dan juga ke mesin Anda. Dengan demikian, kemampuan untuk mengeksekusi skrip yang tertanam dalam HTML dinonaktifkan secara default:
const dom = new JSDOM(`<body> <div id="content"></div> <script>document.getElementById("content").append(document.createElement("hr"));</script> </body>`);// Skrip tidak akan dieksekusi, secara default:console.log(dom.window.document.getElementById("content").children.length); // 0
Untuk mengaktifkan eksekusi skrip di dalam halaman, Anda dapat menggunakan opsi runScripts: "dangerously"
:
const dom = new JSDOM(`<body> <div id="content"></div> <script>document.getElementById("content").append(document.createElement("hr"));</script> </body>`, { runScripts: "dangerously" });// Skrip akan dieksekusi dan memodifikasi DOM:console.log(dom.window.document.getElementById("content").children.length); // 1
Sekali lagi kami tekankan untuk hanya menggunakan ini saat memasukkan kode jsdom yang Anda tahu aman. Jika Anda menggunakannya pada kode sewenang-wenang yang disediakan pengguna, atau kode dari Internet, Anda secara efektif menjalankan kode Node.js yang tidak tepercaya, dan mesin Anda dapat disusupi.
Jika Anda ingin menjalankan skrip eksternal , yang disertakan melalui <script src="">
, Anda juga harus memastikan bahwa skrip tersebut memuatnya. Untuk melakukannya, tambahkan opsi resources: "usable"
seperti dijelaskan di bawah. (Anda mungkin juga ingin menyetel opsi url
, karena alasan yang dibahas di sana.)
Atribut pengendali peristiwa, seperti <div onclick="">
, juga diatur oleh pengaturan ini; mereka tidak akan berfungsi kecuali runScripts
disetel ke "dangerously"
. (Namun, properti pengendali peristiwa, seperti div.onclick = ...
, akan berfungsi terlepas dari runScripts
.)
Jika Anda hanya mencoba menjalankan skrip "dari luar", alih-alih membiarkan elemen <script>
dan atribut pengendali kejadian dijalankan "dari dalam", Anda dapat menggunakan opsi runScripts: "outside-only"
, yang memungkinkan salinan baru dari semua global yang disediakan spesifikasi JavaScript untuk dipasang di window
. Ini mencakup hal-hal seperti window.Array
, window.Promise
, dll. Ini juga, khususnya, mencakup window.eval
, yang memungkinkan menjalankan skrip, tetapi dengan window
jsdom sebagai global:
const dom = new JSDOM(`<body> <div id="content"></div> <script>document.getElementById("content").append(document.createElement("hr"));</script> </body>`, { runScripts: "outside-only" });// menjalankan skrip di luar JSDOM:dom.window.eval('document.getElementById("content").append(document.createElement("p"));');console.log(dom.window.document.getElementById("content"). anak-anak.panjang); // 1console.log(dom.window.document.getElementsByTagName("jam").panjang); // 0console.log(dom.window.document.getElementsByTagName("p").length); // 1
Ini dinonaktifkan secara default karena alasan kinerja, namun aman untuk diaktifkan.
Perhatikan bahwa dalam konfigurasi default, tanpa menyetel runScripts
, nilai window.Array
, window.eval
, dll. akan sama dengan yang disediakan oleh lingkungan luar Node.js. Artinya, window.eval === eval
akan berlaku, jadi window.eval
tidak akan menjalankan skrip dengan cara yang berguna.
Kami sangat menyarankan untuk tidak mencoba "mengeksekusi skrip" dengan menggabungkan lingkungan global jsdom dan Node (misalnya dengan melakukan global.window = dom.window
), dan kemudian mengeksekusi skrip atau kode pengujian di dalam lingkungan global Node. Sebaliknya, Anda harus memperlakukan jsdom seperti Anda memperlakukan browser, dan menjalankan semua skrip dan pengujian yang memerlukan akses ke DOM di dalam lingkungan jsdom, menggunakan window.eval
atau runScripts: "dangerously"
. Hal ini mungkin memerlukan, misalnya, pembuatan bundel browserify untuk dieksekusi sebagai elemen <script>
—seperti yang Anda lakukan di browser.
Terakhir, untuk kasus penggunaan tingkat lanjut, Anda dapat menggunakan metode dom.getInternalVMContext()
, yang didokumentasikan di bawah.
jsdom tidak memiliki kemampuan untuk merender konten visual, dan akan bertindak seperti browser tanpa kepala secara default. Ini memberikan petunjuk ke halaman web melalui API seperti document.hidden
bahwa kontennya tidak terlihat.
Ketika pretendToBeVisual
disetel ke true
, jsdom akan berpura-pura sedang merender dan menampilkan konten. Hal ini dilakukan dengan:
Mengubah document.hidden
menjadi false
bukan true
Mengubah document.visibilityState
untuk mengembalikan "visible"
dan bukan "prerender"
Mengaktifkan metode window.requestAnimationFrame()
dan window.cancelAnimationFrame()
, yang jika tidak ada tidak ada
const window = (JSDOM baru(``, {pretendToBeVisual: true })).window;window.requestAnimationFrame(timestamp => { console.log(cap waktu > 0);});
Perhatikan bahwa jsdom masih tidak melakukan tata letak atau rendering apa pun, jadi ini sebenarnya hanya tentang berpura-pura menjadi visual, bukan tentang mengimplementasikan bagian-bagian platform yang akan diimplementasikan oleh browser web visual yang sebenarnya.
Secara default, jsdom tidak akan memuat sub-sumber daya apa pun seperti skrip, stylesheet, gambar, atau iframe. Jika Anda ingin jsdom memuat sumber daya tersebut, Anda dapat meneruskan opsi resources: "usable"
, yang akan memuat semua sumber daya yang dapat digunakan. Itu adalah:
Bingkai dan iframe, melalui <frame>
dan <iframe>
Stylesheet, melalui <link rel="stylesheet">
Skrip, melalui <script>
, tetapi hanya jika runScripts: "dangerously"
juga disetel
Gambar, melalui <img>
, tetapi hanya jika paket canvas
npm juga diinstal (lihat "Dukungan Canvas" di bawah)
Saat mencoba memuat sumber daya, ingatlah bahwa nilai default untuk opsi url
adalah "about:blank"
, yang berarti sumber daya apa pun yang disertakan melalui URL relatif akan gagal dimuat. (Hasil dari mencoba mengurai URL /something
terhadap URL about:blank
adalah sebuah kesalahan.) Jadi, Anda mungkin ingin menetapkan nilai non-default untuk opsi url
dalam kasus tersebut, atau gunakan salah satu dari kenyamanan API yang melakukannya secara otomatis.
Untuk lebih menyesuaikan perilaku pemuatan sumber daya jsdom, Anda dapat meneruskan instance kelas ResourceLoader
sebagai nilai opsi resources
:
const resourceLoader = jsdom baru.ResourceLoader({ proksi: "http://127.0.0.1:9001", strictSSL: salah, userAgent: "Mellblomenator/9000",});const dom = new JSDOM(``, { resources: resourceLoader });
Tiga opsi pada konstruktor ResourceLoader
adalah:
proxy
adalah alamat proxy HTTP yang akan digunakan.
strictSSL
dapat disetel ke false untuk menonaktifkan persyaratan validitas sertifikat SSL.
userAgent
mempengaruhi header User-Agent
yang dikirim, dan dengan demikian nilai yang dihasilkan untuk navigator.userAgent
. Standarnya adalah `Mozilla/5.0 (${process.platform || "unknown OS"}) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/${jsdomVersion}`
.
Anda dapat menyesuaikan pengambilan sumber daya lebih lanjut dengan membuat subkelas ResourceLoader
dan mengganti metode fetch()
. Misalnya, berikut adalah versi yang menggantikan respons yang diberikan untuk URL tertentu:
kelas CustomResourceLoader memperluas jsdom.ResourceLoader { ambil(url, opsi) {// Ganti konten skrip ini untuk melakukan sesuatu yang tidak biasa.if (url === "https://example.com/some-special-script.js") { return Promise.resolve( Buffer.from("window.someGlobal = 5;"));}return super.fetch(url, opsi); }}
jsdom akan memanggil metode fetch()
pemuat sumber daya khusus Anda setiap kali ia menemukan sumber daya yang "dapat digunakan", sesuai bagian di atas. Metode ini mengambil string URL, serta beberapa opsi yang harus Anda lewati tanpa dimodifikasi jika memanggil super.fetch()
. Itu harus mengembalikan janji untuk objek Buffer
Node.js, atau mengembalikan null
jika sumber daya sengaja tidak dimuat. Secara umum, sebagian besar kasus ingin didelegasikan ke super.fetch()
, seperti yang ditunjukkan.
Salah satu opsi yang akan Anda terima di fetch()
adalah elemen (jika ada) yang mengambil sumber daya.
kelas CustomResourceLoader memperluas jsdom.ResourceLoader { ambil(url, opsi) {if (options.element) { console.log(`Element ${options.element.localName} meminta url ${url}`);}return super.fetch(url, options); }}
Seperti halnya browser web, jsdom memiliki konsep "konsol". Ini mencatat informasi yang dikirim langsung dari halaman, melalui skrip yang dijalankan di dalam dokumen, serta informasi dari implementasi jsdom itu sendiri. Kami menyebut konsol yang dapat dikontrol pengguna sebagai "konsol virtual", untuk membedakannya dari API console
Node.js dan dari API window.console
di dalam halaman.
Secara default, konstruktor JSDOM
akan mengembalikan sebuah instance dengan konsol virtual yang meneruskan semua outputnya ke konsol Node.js. Untuk membuat konsol virtual Anda sendiri dan meneruskannya ke jsdom, Anda dapat mengganti default ini dengan melakukan
const virtualConsole = jsdom baru.VirtualConsole();const dom = JSDOM baru(``, { virtualConsole });
Kode seperti ini akan membuat konsol virtual tanpa perilaku. Anda dapat memberikan perilaku dengan menambahkan event pendengar untuk semua metode konsol yang mungkin:
virtualConsole.on("error", () => { ... });virtualConsole.on("warn", () => { ... });virtualConsole.on("info", () => { ... });virtualConsole.on("dir", () => { ... });// ... dll. Lihat https://console.spec.whatwg.org/#logging
(Perhatikan bahwa mungkin yang terbaik adalah menyiapkan pendengar peristiwa ini sebelum memanggil new JSDOM()
, karena kesalahan atau skrip pemanggil konsol mungkin terjadi selama penguraian.)
Jika Anda hanya ingin mengalihkan keluaran konsol virtual ke konsol lain, seperti konsol Node.js default, Anda dapat melakukannya
virtualConsole.sendTo(konsol);
Ada juga acara khusus, "jsdomError"
, yang akan diaktifkan dengan objek kesalahan untuk melaporkan kesalahan dari jsdom itu sendiri. Hal ini serupa dengan pesan kesalahan yang sering muncul di konsol browser web, meskipun pesan tersebut tidak dimulai oleh console.error
. Sejauh ini, kesalahan berikut dihasilkan dengan cara ini:
Kesalahan saat memuat atau menguraikan subsumber daya (skrip, lembar gaya, bingkai, dan iframe)
Kesalahan eksekusi skrip yang tidak ditangani oleh event handler onerror
jendela yang mengembalikan true
atau memanggil event.preventDefault()
Kesalahan yang tidak diterapkan akibat panggilan ke metode, seperti window.alert
, yang tidak diimplementasikan oleh jsdom, tetapi tetap diinstal untuk kompatibilitas web
Jika Anda menggunakan sendTo(c)
untuk mengirim kesalahan ke c
, secara default ia akan memanggil c.error(errorStack[, errorDetail])
dengan informasi dari peristiwa "jsdomError"
. Jika Anda lebih suka mempertahankan pemetaan peristiwa satu-ke-satu yang ketat ke pemanggilan metode, dan mungkin menangani sendiri "jsdomError"
, maka Anda dapat melakukannya
virtualConsole.sendTo(c, { menghilangkanJSDOMErrors: benar });
Seperti browser web, jsdom memiliki konsep toples cookie, yang menyimpan cookie HTTP. Cookie yang memiliki URL pada domain yang sama dengan dokumen, dan tidak ditandai hanya HTTP, dapat diakses melalui API document.cookie
. Selain itu, semua cookie di toples cookie akan memengaruhi pengambilan subsumber daya.
Secara default, konstruktor JSDOM
akan mengembalikan sebuah instance dengan toples cookie kosong. Untuk membuat toples cookie Anda sendiri dan meneruskannya ke jsdom, Anda dapat mengganti default ini dengan melakukan
const cookieJar = jsdom baru.CookieJar(toko, opsi);const dom = JSDOM baru(``, { cookieJar });
Ini sangat berguna jika Anda ingin berbagi toples cookie yang sama di antara beberapa jsdom, atau mengisi toples cookie dengan nilai tertentu sebelumnya.
Stoples kue disediakan dalam paket kue keras. Konstruktor jsdom.CookieJar
adalah subkelas dari toples cookie tangguh yang secara default menyetel opsi looseMode: true
, karena opsi tersebut lebih cocok dengan perilaku browser. Jika Anda ingin menggunakan sendiri utilitas dan kelas hard-cookie, Anda dapat menggunakan ekspor modul jsdom.toughCookie
untuk mendapatkan akses ke instance modul hard-cookie yang dikemas dengan jsdom.
jsdom memungkinkan Anda untuk melakukan intervensi dalam pembuatan jsdom sejak awal: setelah objek Window
dan Document
dibuat, tetapi sebelum HTML apa pun diurai untuk mengisi dokumen dengan node:
const dom = JSDOM baru(`<p>Halo</p>`, { beforeParse(window) {window.document.childNodes.length === 0;window.someCoolAPI = () => { /* ... */ }; }});
Ini sangat berguna jika Anda ingin memodifikasi lingkungan dengan cara tertentu, misalnya menambahkan shim untuk API platform web yang tidak didukung jsdom.
JSDOM
Setelah Anda membuat objek JSDOM
, objek tersebut akan memiliki kemampuan berguna berikut:
window
properti mengambil objek Window
yang dibuat untuk Anda.
Properti virtualConsole
dan cookieJar
mencerminkan opsi yang Anda berikan, atau default yang dibuat untuk Anda jika tidak ada yang diteruskan untuk opsi tersebut.
serialize()
Metode serialize()
akan mengembalikan serialisasi HTML dokumen, termasuk doctype:
const dom = new JSDOM(`<!DOCTYPE html>hello`);dom.serialize() === "<!DOCTYPE html><html><head></head><body>halo</body></ html>";// Kontras dengan:dom.window.document.documentElement.outerHTML === "<html><head></head><body>halo</body></html>";
nodeLocation(node)
Metode nodeLocation()
akan menemukan lokasi node DOM di dalam dokumen sumber, dan mengembalikan informasi lokasi parse5 untuk node tersebut:
const dom = JSDOM baru( `<p>Halo <img src="foo.jpg"> </p>`, { includeNodeLocations: true });const document = dom.window.document;const bodyEl = document.body; // secara implisit dibuatconst pEl = document.querySelector("p");const textNode = pEl.firstChild;const imgEl = document.querySelector("img");console.log(dom.nodeLocation(bodyEl)); // batal; itu tidak ada di 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 }
Perhatikan bahwa fitur ini hanya berfungsi jika Anda telah menyetel opsi includeNodeLocations
; lokasi node dinonaktifkan secara default karena alasan kinerja.
vm
menggunakan getInternalVMContext()
Modul vm
bawaan dari Node.js adalah yang mendasari keajaiban menjalankan skrip jsdom. Beberapa kasus penggunaan tingkat lanjut, seperti pra-kompilasi skrip dan kemudian menjalankannya beberapa kali, mendapat manfaat dari penggunaan modul vm
secara langsung dengan Window
yang dibuat oleh jsdom.
Untuk mendapatkan akses ke objek global terkonteksifikasi, yang cocok untuk digunakan dengan API vm
, Anda dapat menggunakan metode getInternalVMContext()
:
const { Skrip } = memerlukan("vm");const dom = JSDOM baru(``, { runScripts: "hanya di luar" });const skrip = Skrip baru(` if (!ini.ran) { ini.ran = 0; } ++ini.ran;`);const vmContext = dom.getInternalVMContext();script.runInContext(vmContext);script.runInContext(vmContext);script.runInContext(vmContext);console.assert(dom.window.ran === 3);
Ini adalah fungsi yang agak canggih, dan kami menyarankan untuk tetap menggunakan API DOM normal (seperti window.eval()
atau document.createElement("script")
) kecuali Anda memiliki kebutuhan yang sangat spesifik.
Perhatikan bahwa metode ini akan memunculkan pengecualian jika instance JSDOM
dibuat tanpa set runScripts
, atau jika Anda menggunakan jsdom di browser web.
reconfigure(settings)
Properti top
di window
ditandai [Unforgeable]
dalam spesifikasi, yang berarti properti tersebut tidak dapat dikonfigurasi dan karenanya tidak dapat ditimpa atau dibayangi oleh kode normal yang berjalan di dalam jsdom, bahkan menggunakan Object.defineProperty
.
Demikian pula, saat ini jsdom tidak menangani navigasi (seperti pengaturan window.location.href = "https://example.com/"
); melakukan hal ini akan menyebabkan konsol virtual mengeluarkan "jsdomError"
yang menjelaskan bahwa fitur ini tidak diterapkan, dan tidak ada yang berubah: tidak akan ada objek Window
atau Document
baru, dan objek location
window
yang ada akan tetap sama nilai properti.
Namun, jika Anda bertindak dari luar jendela, misalnya dalam beberapa kerangka pengujian yang membuat jsdom, Anda dapat mengganti salah satu atau keduanya menggunakan metode reconfigure()
khusus:
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/";
Perhatikan bahwa mengubah URL jsdom akan berdampak pada semua API yang mengembalikan URL dokumen saat ini, seperti window.location
, document.URL
, dan document.documentURI
, serta resolusi URL relatif dalam dokumen, dan pemeriksaan asal yang sama dan perujuk yang digunakan saat mengambil subsumber daya. Namun, ia tidak akan melakukan navigasi ke konten URL tersebut; konten DOM akan tetap tidak berubah, dan tidak ada instance baru dari Window
, Document
, dll. yang akan dibuat.
fromURL()
Selain konstruktor JSDOM
itu sendiri, jsdom menyediakan metode pabrik yang mengembalikan janji untuk membuat jsdom dari URL:
JSDOM.fromURL("https://example.com/", opsi).lalu(dom => { konsol.log(dom.serialize());});
Janji yang dikembalikan akan dipenuhi dengan instance JSDOM
jika URL-nya valid dan permintaan berhasil. Pengalihan apa pun akan diikuti ke tujuan akhirnya.
Opsi yang diberikan ke fromURL()
serupa dengan opsi yang diberikan ke konstruktor JSDOM
, dengan batasan dan konsekuensi tambahan berikut:
Opsi url
dan contentType
tidak dapat disediakan.
Opsi referrer
digunakan sebagai tajuk permintaan Referer
HTTP dari permintaan awal.
Opsi resources
juga memengaruhi permintaan awal; ini berguna jika Anda ingin, misalnya, mengkonfigurasi proxy (lihat di atas).
URL jsdom, tipe konten, dan perujuk yang dihasilkan ditentukan dari respons.
Cookie apa pun yang disetel melalui header respons HTTP Set-Cookie
disimpan di toples cookie jsdom. Demikian pula, cookie apa pun yang sudah ada dalam toples cookie yang disediakan dikirim sebagai header permintaan Cookie
HTTP.
fromFile()
Mirip dengan fromURL()
, jsdom juga menyediakan metode pabrik fromFile()
untuk membuat jsdom dari nama file:
JSDOM.fromFile("stuff.html", opsi).lalu(dom => { konsol.log(dom.serialize());});
Janji yang dikembalikan akan dipenuhi dengan instance JSDOM
jika file yang diberikan dapat dibuka. Seperti biasa di API Node.js, nama file diberikan relatif terhadap direktori kerja saat ini.
Opsi yang disediakan untuk fromFile()
mirip dengan yang disediakan untuk konstruktor JSDOM
, dengan default tambahan berikut:
Opsi url
akan default ke URL file yang sesuai dengan nama file yang diberikan, bukan "about:blank"
.
Opsi contentType
akan default ke "application/xhtml+xml"
jika nama file yang diberikan diakhiri dengan .xht
, .xhtml
, atau .xml
; jika tidak maka akan tetap default ke "text/html"
.
fragment()
Untuk kasus yang paling sederhana, Anda mungkin tidak memerlukan seluruh instance JSDOM
dengan semua kekuatan yang terkait. Anda bahkan mungkin tidak memerlukan Window
atau Document
! Sebagai gantinya, Anda hanya perlu mengurai beberapa HTML, dan mendapatkan objek DOM yang dapat Anda manipulasi. Untuk itu, kami memiliki fragment()
, yang membuat DocumentFragment
dari string tertentu:
const frag = JSDOM.fragment(`<p>Halo</p><p><strong>Hai!</strong>`);frag.childNodes.length === 2;frag.querySelector("strong"). textContent === "Hai!";// dll.
Di sini frag
adalah instance DocumentFragment
, yang isinya dibuat dengan mengurai string yang disediakan. Penguraian dilakukan menggunakan elemen <template>
, sehingga Anda dapat memasukkan elemen apa pun di sana (termasuk elemen dengan aturan penguraian aneh seperti <td>
). Penting juga untuk dicatat bahwa DocumentFragment
yang dihasilkan tidak akan memiliki konteks penelusuran terkait: yaitu, ownerDocument
elemen akan memiliki properti defaultView
nol, sumber daya tidak akan dimuat, dll.
Semua pemanggilan pabrik fragment()
menghasilkan DocumentFragment
yang berbagi pemilik templat yang sama Document
. Hal ini memungkinkan banyak panggilan ke fragment()
tanpa overhead tambahan. Namun ini juga berarti bahwa panggilan ke fragment()
tidak dapat dikustomisasi dengan opsi apa pun.
Perhatikan bahwa serialisasi tidak semudah dengan DocumentFragment
seperti halnya dengan objek JSDOM
lengkap. Jika Anda perlu membuat serialisasi DOM Anda, Anda mungkin harus menggunakan konstruktor JSDOM
secara lebih langsung. Namun untuk kasus khusus sebuah fragmen yang berisi satu elemen, hal ini cukup mudah dilakukan melalui cara biasa:
const frag = JSDOM.fragment(`<p>Halo</p>`);console.log(frag.firstChild.outerHTML); // mencatat "<p>Halo</p>"
jsdom menyertakan dukungan untuk menggunakan paket canvas
untuk memperluas elemen <canvas>
apa pun dengan API kanvas. Agar ini berfungsi, Anda perlu menyertakan canvas
sebagai dependensi dalam proyek Anda, sebagai rekan dari jsdom
. Jika jsdom dapat menemukan paket canvas
, ia akan menggunakannya, tetapi jika tidak ada, elemen <canvas>
akan berperilaku seperti <div>
s. Sejak jsdom v13, canvas
versi 2.x diperlukan; versi 1.x tidak lagi didukung.
Selain menyediakan string, konstruktor JSDOM
juga dapat menyediakan data biner, dalam bentuk Node.js Buffer
atau tipe data biner JavaScript standar seperti ArrayBuffer
, Uint8Array
, DataView
, dll. Ketika ini selesai, jsdom akan mengendus pengkodean dari byte yang disediakan, memindai tag <meta charset>
seperti yang dilakukan browser.
Jika opsi contentType
yang disediakan berisi parameter charset
, pengkodean tersebut akan menggantikan pengkodean yang diendus—kecuali jika ada BOM UTF-8 atau UTF-16, dalam hal ini yang diutamakan. (Sekali lagi, ini seperti browser.)
Pengkodean sniffing ini juga berlaku untuk JSDOM.fromFile()
dan JSDOM.fromURL()
. Dalam kasus terakhir, setiap header Content-Type
yang dikirim bersama respons akan diprioritaskan, dengan cara yang sama seperti opsi contentType
konstruktor.
Perhatikan bahwa dalam banyak kasus, memasok byte dengan cara ini bisa lebih baik daripada memasok string. Misalnya, jika Anda mencoba menggunakan API buffer.toString("utf-8")
Node.js, Node.js tidak akan menghapus BOM terdepan. Jika Anda kemudian memberikan string ini ke jsdom, ia akan menafsirkannya kata demi kata, membiarkan BOM tetap utuh. Namun kode decoding data biner jsdom akan menghapus BOM terkemuka, seperti halnya browser; dalam kasus seperti ini, penyediaan buffer
secara langsung akan memberikan hasil yang diinginkan.
Pengatur waktu di jsdom (ditetapkan oleh window.setTimeout()
atau window.setInterval()
), menurut definisi, akan mengeksekusi kode di masa mendatang dalam konteks jendela. Karena tidak ada cara untuk mengeksekusi kode di masa depan tanpa menjaga proses tetap hidup, pengatur waktu jsdom yang luar biasa akan menjaga proses Node.js Anda tetap hidup. Demikian pula, karena tidak ada cara untuk mengeksekusi kode dalam konteks suatu objek tanpa menjaga objek tersebut tetap hidup, pengatur waktu jsdom yang luar biasa akan mencegah pengumpulan sampah di jendela yang dijadwalkan.
Jika Anda ingin mematikan jendela jsdom, gunakan window.close()
, yang akan menghentikan semua pengatur waktu yang berjalan (dan juga menghapus semua pendengar peristiwa di jendela dan dokumen).
Di Node.js Anda dapat men-debug program menggunakan Chrome DevTools. Lihat dokumentasi resmi untuk cara memulai.
Secara default, elemen jsdom diformat sebagai objek JS biasa di konsol. Untuk mempermudah proses debug, Anda dapat menggunakan jsdom-devtools-formatter, yang memungkinkan Anda memeriksanya seperti elemen DOM sebenarnya.
Orang sering mengalami masalah dengan pemuatan skrip asinkron saat menggunakan jsdom. Banyak halaman memuat skrip secara asinkron, namun tidak ada cara untuk mengetahui kapan skrip tersebut selesai, dan kapan saat yang tepat untuk menjalankan kode Anda dan memeriksa struktur DOM yang dihasilkan. Ini adalah batasan mendasar; kami tidak dapat memprediksi apa yang akan dilakukan skrip pada halaman web, sehingga tidak dapat memberi tahu Anda kapan skrip tersebut selesai memuat lebih banyak skrip.
Hal ini dapat diatasi dengan beberapa cara. Cara terbaik, jika Anda mengontrol halaman yang dimaksud, adalah dengan menggunakan mekanisme apa pun yang diberikan oleh pemuat skrip untuk mendeteksi kapan pemuatan selesai. Misalnya, jika Anda menggunakan pemuat modul seperti RequireJS, kodenya akan terlihat seperti:
// Di sisi Node.js:const window = (new JSDOM(...)).window;window.onModulesLoaded = () => { console.log("siap diluncurkan!");};
<!-- Di dalam HTML yang Anda berikan ke jsdom --><script>requirejs(["entry-module"], () => { window.onModulesLoaded();});</script>
Jika Anda tidak mengontrol halaman, Anda dapat mencoba solusi seperti melakukan polling untuk mengetahui keberadaan elemen tertentu.
Untuk lebih jelasnya lihat pembahasan di #640, terutama komentar mendalam @matthewkastor.
Meskipun kami senang menambahkan fitur baru ke jsdom dan selalu memperbaruinya dengan spesifikasi web terbaru, banyak API yang hilang. Silakan mengajukan masalah jika ada yang kurang, namun kami adalah tim kecil dan sibuk, jadi permintaan penarikan mungkin akan bekerja lebih baik.
Beberapa fitur jsdom disediakan oleh dependensi kami. Dokumentasi penting dalam hal ini mencakup daftar pemilih CSS yang didukung untuk mesin pemilih CSS kami, nwsapi
.
Selain fitur-fitur yang belum kami dapatkan, ada dua fitur utama yang saat ini berada di luar cakupan jsdom. Ini adalah:
Navigasi : kemampuan untuk mengubah objek global, dan semua objek lainnya, saat mengklik link atau menetapkan location.href
atau serupa.
Tata Letak : kemampuan untuk menghitung di mana elemen akan ditata secara visual sebagai hasil dari CSS, yang memengaruhi metode seperti getBoundingClientRects()
atau properti seperti offsetTop
.
Saat ini jsdom memiliki perilaku dummy untuk beberapa aspek fitur ini, seperti mengirimkan "jsdomError"
yang "tidak diterapkan" ke konsol virtual untuk navigasi, atau mengembalikan angka nol untuk banyak properti terkait tata letak. Seringkali Anda dapat mengatasi keterbatasan ini dalam kode Anda, misalnya dengan membuat instance JSDOM
baru untuk setiap halaman yang Anda "navigasi" selama perayapan, atau menggunakan Object.defineProperty()
untuk mengubah berbagai pengambil dan metode terkait tata letak yang dikembalikan.
Perhatikan bahwa alat lain di ruang yang sama, seperti PhantomJS, mendukung fitur ini. Di wiki, kami memiliki artikel yang lebih lengkap tentang jsdom vs. PhantomJS.
jsdom adalah proyek berbasis komunitas yang dikelola oleh tim sukarelawan. Anda dapat mendukung jsdom dengan:
Mendapatkan dukungan profesional untuk jsdom sebagai bagian dari langganan Tidelift. Tidelift membantu menjadikan open source berkelanjutan bagi kami sekaligus memberikan jaminan kepada tim untuk pemeliharaan, perizinan, dan keamanan.
Berkontribusi langsung pada proyek.
Jika Anda memerlukan bantuan dengan jsdom, silakan gunakan salah satu tempat berikut:
Milis (terbaik untuk pertanyaan "bagaimana caranya")
Pelacak masalah (terbaik untuk laporan bug)
Ruang Matriks: #jsdom:matrix.org