jsdom เป็นการนำ JavaScript มาใช้ตามมาตรฐานเว็บหลายๆ มาตรฐาน โดยเฉพาะมาตรฐาน WHATWG DOM และ HTML เพื่อใช้กับ Node.js โดยทั่วไป เป้าหมายของโปรเจ็กต์นี้คือการจำลองชุดย่อยของเว็บเบราว์เซอร์ให้เพียงพอเพื่อเป็นประโยชน์สำหรับการทดสอบและการคัดลอกแอปพลิเคชันเว็บในโลกแห่งความเป็นจริง
jsdom เวอร์ชันล่าสุดต้องใช้ Node.js v18 หรือใหม่กว่า (เวอร์ชันของ jsdom ที่ต่ำกว่า v23 ยังคงใช้งานได้กับ Node.js เวอร์ชันก่อนหน้า แต่ไม่รองรับ)
const jsdom = ต้องการ ("jsdom"); const { JSDOM } = jsdom;
หากต้องการใช้ jsdom คุณจะต้องใช้ตัวสร้าง JSDOM
เป็นหลัก ซึ่งเป็นการเอ็กซ์พอร์ตที่มีชื่อของโมดูลหลัก jsdom ส่งผ่านตัวสร้างสตริง คุณจะได้รับออบเจ็กต์ JSDOM
กลับคืนมา ซึ่งมีคุณสมบัติที่มีประโยชน์มากมาย โดยเฉพาะ window
:
const dom = new JSDOM(`<!DOCTYPE html><p>สวัสดีชาวโลก</p>`);console.log(dom.window.document.querySelector("p").textContent); // "สวัสดีชาวโลก"
(โปรดทราบว่า jsdom จะแยกวิเคราะห์ HTML ที่คุณส่งผ่านเหมือนกับที่เบราว์เซอร์ทำ รวมถึงแท็ก <html>
, <head>
และ <body>
โดยนัย)
ออบเจ็กต์ผลลัพธ์คืออินสแตนซ์ของคลาส JSDOM
ซึ่งมีคุณสมบัติและวิธีการที่เป็นประโยชน์มากมายนอกเหนือจาก window
โดยทั่วไป สามารถใช้เพื่อดำเนินการกับ jsdom จาก "ภายนอก" ได้ โดยทำสิ่งที่ไม่สามารถทำได้ด้วย DOM API ปกติ สำหรับกรณีทั่วไปที่คุณไม่ต้องการฟังก์ชันการทำงานใดๆ นี้ เราขอแนะนำรูปแบบการเขียนโค้ด เช่น
const { window } = new JSDOM(`...`);// หรือ Evenconst { document } = (new JSDOM(`...`)).window;
เอกสารฉบับเต็มเกี่ยวกับทุกสิ่งที่คุณสามารถทำได้ด้วยคลาส JSDOM
อยู่ด้านล่างในส่วน " JSDOM
Object API"
ตัวสร้าง JSDOM
ยอมรับพารามิเตอร์ตัวที่สองซึ่งสามารถใช้เพื่อปรับแต่ง jsdom ของคุณด้วยวิธีต่อไปนี้
const dom = JSDOM ใหม่ (``, { URL: "https://example.org/", ผู้อ้างอิง: "https://example.com/", ประเภทเนื้อหา: "ข้อความ/html", includeNodeLocations: จริง โควต้าพื้นที่เก็บข้อมูล: 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
ถูกส่งออกไป ตามค่าเริ่มต้น ระบบจะตั้งค่าไว้ที่ 5,000,000 หน่วยโค้ดต่อต้นทาง ตามที่ได้รับแรงบันดาลใจจากข้อกำหนด HTML
โปรดทราบว่าทั้ง url
และ referrer
จะถูกกำหนดรูปแบบมาตรฐานก่อนที่จะใช้งาน ดังนั้น เช่น หากคุณผ่านใน "https:example.com"
jsdom จะตีความราวกับว่าคุณได้ให้ "https://example.com/"
หากคุณส่ง URL ที่ไม่สามารถแยกวิเคราะห์ได้ การโทรจะเกิดขึ้น (URL จะถูกแยกวิเคราะห์และเรียงลำดับตามมาตรฐาน URL)
ความสามารถที่ทรงพลังที่สุดของ jsdom คือสามารถรันสคริปต์ภายใน jsdom ได้ สคริปต์เหล่านี้สามารถแก้ไขเนื้อหาของเพจและเข้าถึงการใช้งาน jsdom API ของแพลตฟอร์มเว็บทั้งหมดได้
อย่างไรก็ตาม สิ่งนี้ก็เป็นอันตรายเช่นกันเมื่อต้องรับมือกับเนื้อหาที่ไม่น่าเชื่อถือ แซนด์บ็อกซ์ jsdom ไม่สามารถเข้าใจผิดได้ และโค้ดที่ทำงานภายใน <script>
ของ DOM สามารถเข้าถึงสภาพแวดล้อม Node.js และเข้าถึงเครื่องของคุณได้ หากพยายามมากพอ ด้วยเหตุนี้ ความสามารถในการรันสคริปต์ที่ฝังอยู่ใน HTML จึงถูกปิดใช้งานตามค่าเริ่มต้น:
const dom = ใหม่ 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 = ใหม่ 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"
ซึ่งเปิดใช้งานสำเนาใหม่ของ globals ที่ระบุโดย JavaScript ทั้งหมดที่จะติดตั้งบน window
ซึ่งรวมถึงสิ่งต่าง ๆ เช่น window.Array
, window.Promise
ฯลฯ นอกจากนี้ยังมี window.eval
ซึ่งอนุญาตให้เรียกใช้สคริปต์ แต่มี window
jsdom เป็น global:
const dom = ใหม่ 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 เหมือนที่คุณทำกับเบราว์เซอร์ และเรียกใช้สคริปต์และการทดสอบทั้งหมดที่ต้องการเข้าถึง DOM ภายในสภาพแวดล้อม jsdom โดยใช้ window.eval
หรือ runScripts: "dangerously"
ซึ่งอาจจำเป็นต้องมีการสร้างบันเดิล browserify เพื่อดำเนินการเป็นองค์ประกอบ <script>
เช่นเดียวกับที่คุณทำในเบราว์เซอร์
สุดท้ายนี้ สำหรับกรณีการใช้งานขั้นสูง คุณสามารถใช้เมธอด dom.getInternalVMContext()
ดังที่บันทึกไว้ด้านล่าง
jsdom ไม่มีความสามารถในการแสดงเนื้อหาภาพ และจะทำงานเหมือนเบราว์เซอร์ที่ไม่มีส่วนหัวตามค่าเริ่มต้น โดยให้คำแนะนำแก่หน้าเว็บผ่าน API เช่น document.hidden
ว่าเนื้อหาไม่สามารถมองเห็นได้
เมื่อตั้งค่าตัวเลือก pretendToBeVisual
เป็น true
jsdom จะแกล้งทำเป็นว่ากำลังแสดงผลและแสดงเนื้อหา มันทำได้โดย:
การเปลี่ยน document.hidden
ให้คืนค่า false
แทนที่จะเป็น true
การเปลี่ยน document.visibilityState
เพื่อส่งคืน "visible"
แทนที่จะเป็น "prerender"
การเปิดใช้งานเมธอด window.requestAnimationFrame()
และ window.cancelAnimationFrame()
ซึ่งไม่มีอยู่จริง
const window = (JSDOM ใหม่ (``, {แกล้งToBeVisual: true })).window;window.requestAnimationFrame(ประทับเวลา => { console.log(ประทับเวลา > 0);});
โปรดทราบว่า jsdom ยังไม่ได้ทำเลย์เอาต์หรือการเรนเดอร์ใดๆ ดังนั้นนี่เป็นเพียง การแกล้ง ทำเป็นภาพเท่านั้น ไม่ใช่เกี่ยวกับการนำส่วนต่างๆ ของแพลตฟอร์มไปใช้แบบที่เว็บเบราว์เซอร์แบบเห็นภาพจริงจะนำไปใช้
ตามค่าเริ่มต้น jsdom จะไม่โหลดทรัพยากรย่อยใดๆ เช่น สคริปต์ สไตล์ชีต รูปภาพ หรือ iframe หากคุณต้องการให้ jsdom โหลดทรัพยากรดังกล่าว คุณสามารถส่ง resources: "usable"
ซึ่งจะโหลดทรัพยากรที่ใช้งานได้ทั้งหมด ได้แก่:
เฟรมและ iframe ผ่าน <frame>
และ <iframe>
สไตล์ชีทผ่าน <link rel="stylesheet">
สคริปต์ผ่าน <script>
แต่เฉพาะในกรณีที่ตั้งค่า runScripts: "dangerously"
เท่านั้น
รูปภาพผ่าน <img>
แต่เฉพาะเมื่อมีการติดตั้งแพ็คเกจ canvas
npm ด้วย (ดู "การสนับสนุน Canvas" ด้านล่าง)
เมื่อพยายามโหลดทรัพยากร โปรดทราบว่าค่าเริ่มต้นสำหรับตัวเลือก url
คือ "about:blank"
ซึ่งหมายความว่าทรัพยากรใดๆ ที่รวมอยู่ใน URL ที่เกี่ยวข้องจะไม่สามารถโหลดได้ (ผลลัพธ์ของการพยายามแยกวิเคราะห์ URL /something
กับ URL about:blank
ถือเป็นข้อผิดพลาด) ดังนั้น คุณอาจต้องการตั้งค่าที่ไม่ใช่ค่าเริ่มต้นสำหรับตัวเลือก url
ในกรณีเหล่านั้น หรือใช้ช่องทางที่สะดวกอย่างใดอย่างหนึ่ง API ที่ทำโดยอัตโนมัติ
หากต้องการปรับแต่งพฤติกรรมการโหลดทรัพยากรของ jsdom ได้อย่างเต็มที่ยิ่งขึ้น คุณสามารถส่งอินสแตนซ์ของคลาส ResourceLoader
เป็นค่าตัวเลือก resources
ได้:
const resourcesLoader = jsdom.ResourceLoader ใหม่ ({ พร็อกซี: "http://127.0.0.1:9001", SSL ที่เข้มงวด: เท็จ userAgent: "Mellblomenator/9000",});const dom = new JSDOM(``, { ทรัพยากร: resourcesLoader });
สามตัวเลือกสำหรับตัวสร้าง ResourceLoader
คือ:
proxy
คือที่อยู่ของพร็อกซี HTTP ที่จะใช้
strictSSL
สามารถตั้งค่าเป็นเท็จเพื่อปิดการใช้งานข้อกำหนดที่ใบรับรอง 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) {// แทนที่เนื้อหาของสคริปต์นี้เพื่อทำสิ่งผิดปกติถ้า (url === "https://example.com/some-special-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} is requesting the url ${url}`);}return super.fetch(url, options); -
เช่นเดียวกับเว็บเบราว์เซอร์ jsdom มีแนวคิดของ "คอนโซล" ซึ่งจะบันทึกทั้งข้อมูลที่ส่งโดยตรงจากเพจ ผ่านสคริปต์ที่ดำเนินการภายในเอกสาร รวมถึงข้อมูลจากการใช้งาน jsdom เอง เราเรียกคอนโซลที่ผู้ใช้ควบคุมได้ว่า "คอนโซลเสมือน" เพื่อแยกความแตกต่างจาก console
API ของ Node.js และจาก API ของ window.console
ภายในเพจ
ตามค่าเริ่มต้น ตัวสร้าง JSDOM
จะส่งคืนอินสแตนซ์พร้อมกับคอนโซลเสมือนที่ส่งต่อเอาต์พุตทั้งหมดไปยังคอนโซล Node.js หากต้องการสร้างคอนโซลเสมือนของคุณเองและส่งผ่านไปยัง jsdom คุณสามารถแทนที่ค่าเริ่มต้นนี้ได้โดยดำเนินการ
const virtualConsole = jsdom.VirtualConsole ใหม่ (); const dom = JSDOM ใหม่ (``, { virtualConsole });
โค้ดลักษณะนี้จะสร้างคอนโซลเสมือนโดยไม่มีการทำงานใดๆ คุณสามารถกำหนดพฤติกรรมได้โดยเพิ่มตัวฟังเหตุการณ์สำหรับวิธีการคอนโซลที่เป็นไปได้ทั้งหมด:
virtualConsole.on("ข้อผิดพลาด", () => { ... });virtualConsole.on("warn", () => { ... });virtualConsole.on("ข้อมูล", () => { ... });virtualConsole.on("dir", () => { ... });// ... ฯลฯ ดู https://console.spec.whatwg.org/#logging
(โปรดทราบว่าอาจเป็นการดีที่สุดที่จะตั้งค่า Listener เหตุการณ์เหล่านี้ ก่อนที่ จะเรียก new JSDOM()
เนื่องจากข้อผิดพลาดหรือสคริปต์เรียกใช้คอนโซลอาจเกิดขึ้นระหว่างการแยกวิเคราะห์)
หากคุณเพียงต้องการเปลี่ยนเส้นทางเอาต์พุตคอนโซลเสมือนไปยังคอนโซลอื่น เช่น Node.js เริ่มต้น คุณก็สามารถทำได้
virtualConsole.sendTo (คอนโซล);
นอกจากนี้ยังมีกิจกรรมพิเศษ "jsdomError"
ซึ่งจะเริ่มทำงานพร้อมกับอ็อบเจ็กต์ข้อผิดพลาดเพื่อรายงานข้อผิดพลาดจาก jsdom เอง สิ่งนี้คล้ายกับข้อความแสดงข้อผิดพลาดที่มักปรากฏในคอนโซลของเว็บเบราว์เซอร์ แม้ว่าจะไม่ได้เริ่มต้นโดย console.error
ก็ตาม จนถึงตอนนี้ ข้อผิดพลาดต่อไปนี้จะแสดงในลักษณะนี้:
เกิดข้อผิดพลาดในการโหลดหรือแยกวิเคราะห์ทรัพยากรย่อย (สคริปต์ สไตล์ชีท เฟรม และ iframe)
ข้อผิดพลาดในการเรียกใช้สคริปต์ที่ไม่ได้รับการจัดการโดยตัวจัดการเหตุการณ์ onerror
ของหน้าต่างที่ส่งคืนค่า true
หรือการโทร event.preventDefault()
ข้อผิดพลาดที่ไม่ได้นำไปใช้ซึ่งเกิดจากการเรียกใช้เมธอด เช่น window.alert
ซึ่ง jsdom ไม่ได้ใช้ แต่ติดตั้งต่อไปเพื่อความเข้ากันได้ของเว็บ
หากคุณใช้ sendTo(c)
เพื่อส่งข้อผิดพลาดไปที่ c
ตามค่าเริ่มต้น ระบบจะเรียก c.error(errorStack[, errorDetail])
พร้อมข้อมูลจากเหตุการณ์ "jsdomError"
หากคุณต้องการรักษาการแมปเหตุการณ์แบบหนึ่งต่อหนึ่งอย่างเข้มงวดกับการโทรเมธอด และอาจจัดการ "jsdomError"
ด้วยตัวเอง คุณสามารถทำได้
virtualConsole.sendTo (c, { ละเว้น JSDOMErrors: จริง });
เช่นเดียวกับเว็บเบราว์เซอร์ jsdom มีแนวคิดเกี่ยวกับโถคุกกี้ซึ่งจัดเก็บคุกกี้ HTTP คุกกี้ที่มี URL บนโดเมนเดียวกันกับเอกสาร และไม่ได้ทำเครื่องหมายว่า HTTP เท่านั้น สามารถเข้าถึงได้ผ่าน document.cookie
API นอกจากนี้ คุกกี้ทั้งหมดในโถคุกกี้จะส่งผลต่อการดึงทรัพยากรย่อย
ตามค่าเริ่มต้น ตัวสร้าง JSDOM
จะส่งคืนอินสแตนซ์พร้อมกับโถคุกกี้ที่ว่างเปล่า หากต้องการสร้างโถคุกกี้ของคุณเองและส่งต่อไปยัง jsdom คุณสามารถแทนที่ค่าเริ่มต้นนี้ได้โดยดำเนินการ
const cookieJar = jsdom.CookieJar ใหม่ (ร้านค้า, ตัวเลือก); const dom = JSDOM ใหม่ (``, { cookieJar });
สิ่งนี้มีประโยชน์เป็นส่วนใหญ่หากคุณต้องการแชร์โถคุกกี้เดียวกันระหว่าง jsdom หลายอัน หรือทำให้โถคุกกี้มีค่าที่แน่นอนล่วงหน้า
โถคุกกี้จัดทำโดยแพ็คเกจคุกกี้ที่เหนียว ตัวสร้าง jsdom.CookieJar
เป็นคลาสย่อยของโถคุกกี้คุกกี้ที่ยากซึ่งโดยค่าเริ่มต้นจะตั้งค่าตัวเลือก looseMode: true
เนื่องจากจะตรงกับพฤติกรรมของเบราว์เซอร์ที่ดีกว่า หากคุณต้องการใช้ยูทิลิตี้และคลาสของ Tough-Cookie ด้วยตัวเอง คุณสามารถใช้การส่งออกโมดูล jsdom.toughCookie
เพื่อเข้าถึงอินสแตนซ์โมดูล Tough-Cookie ที่แพ็กเกจด้วย jsdom
jsdom ช่วยให้คุณสามารถแทรกแซงในการสร้าง jsdom ได้ตั้งแต่เนิ่นๆ: หลังจากสร้างอ็อบเจ็กต์ Window
และ Document
แล้ว แต่ก่อนที่จะแยกวิเคราะห์ HTML ใด ๆ เพื่อเติมเอกสารด้วยโหนด:
const dom = JSDOM ใหม่ (`<p>สวัสดี</p>`, { beforeParse(หน้าต่าง) {window.document.childNodes.length === 0;window.someCoolAPI = () => { /* ... */ }; -
สิ่งนี้มีประโยชน์อย่างยิ่งหากคุณต้องการแก้ไขสภาพแวดล้อมในทางใดทางหนึ่ง เช่น การเพิ่ม shims สำหรับแพลตฟอร์มเว็บ APIs jsdom ไม่รองรับ
JSDOM
เมื่อคุณสร้างวัตถุ JSDOM
แล้ว มันจะมีความสามารถที่มีประโยชน์ดังต่อไปนี้:
window
คุณสมบัติดึงวัตถุ Window
ที่สร้างขึ้นสำหรับคุณ
คุณสมบัติ virtualConsole
และ cookieJar
สะท้อนถึงตัวเลือกที่คุณส่งผ่าน หรือค่าเริ่มต้นที่สร้างขึ้นสำหรับคุณ หากไม่มีสิ่งใดถูกส่งเข้ามาสำหรับตัวเลือกเหล่านั้น
serialize()
วิธี serialize()
จะส่งคืนการทำให้เป็นอนุกรม HTML ของเอกสาร รวมถึงประเภทเอกสาร:
const dom = new JSDOM(`<!DOCTYPE html>hello`);dom.serialize() === "<!DOCTYPE html><html><head></head><body>สวัสดี</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)); // { startOffset: 13, endOffset: 32 }
โปรดทราบว่าคุณลักษณะนี้จะใช้งานได้เฉพาะเมื่อคุณตั้งค่าอ็อพชัน includeNodeLocations
เท่านั้น ตำแหน่งโหนดจะถูกปิดโดยค่าเริ่มต้นด้วยเหตุผลด้านประสิทธิภาพ
vm
โดยใช้ getInternalVMContext()
โมดูล vm
ในตัวของ Node.js คือสิ่งที่สนับสนุนความมหัศจรรย์ของการรันสคริปต์ของ jsdom กรณีการใช้งานขั้นสูงบางกรณี เช่น การคอมไพล์สคริปต์ล่วงหน้าแล้วเรียกใช้งานหลายครั้ง จะได้ประโยชน์จากการใช้โมดูล vm
โดยตรงกับ Window
ที่สร้างด้วย jsdom
หากต้องการเข้าถึงออบเจ็กต์โกลบอลที่มีบริบทซึ่งเหมาะสำหรับใช้กับ vm
API คุณสามารถใช้เมธอด getInternalVMContext()
ได้:
const { สคริปต์ } = ต้องการ ("vm"); const dom = new JSDOM (``, { runScripts: "ภายนอกเท่านั้น" }); const 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
หรือหากคุณใช้ jsdom ในเว็บเบราว์เซอร์
reconfigure(settings)
คุณสมบัติ top
บน window
ถูกทำเครื่องหมายเป็น [Unforgeable]
ในข้อมูลจำเพาะ ซึ่งหมายความว่าเป็นคุณสมบัติของตัวเองที่ไม่สามารถกำหนดค่าได้ และดังนั้นจึงไม่สามารถแทนที่หรือบดบังด้วยโค้ดปกติที่ทำงานภายใน jsdom ได้ แม้จะใช้ Object.defineProperty
ก็ตาม
ในทำนองเดียวกัน ในปัจจุบัน jsdom ไม่รองรับการนำทาง (เช่น การตั้งค่า window.location.href = "https://example.com/"
); การทำเช่นนี้จะทำให้คอนโซลเสมือนปล่อย "jsdomError"
โดยอธิบายว่าฟีเจอร์นี้ไม่ได้ถูกนำมาใช้ และจะไม่มีอะไรเปลี่ยนแปลง: จะไม่มี Window
หรือ Document
ใหม่และวัตถุ location
ของ window
ที่มีอยู่จะยังคงเหมือนเดิมทั้งหมด ค่าทรัพย์สิน
อย่างไรก็ตาม หากคุณดำเนินการจากนอกหน้าต่าง เช่น ในเฟรมเวิร์กการทดสอบบางตัวที่สร้าง 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/";
โปรดทราบว่าการเปลี่ยน URL ของ jsdom จะส่งผลต่อ API ทั้งหมดที่ส่งคืน URL ของเอกสารปัจจุบัน เช่น window.location
, document.URL
และ document.documentURI
รวมถึงการแก้ไข URL ที่เกี่ยวข้องภายในเอกสาร และการตรวจสอบที่มาเดียวกัน และผู้อ้างอิงที่ใช้ในขณะที่ดึงทรัพยากรย่อย อย่างไรก็ตาม จะไม่ดำเนินการนำทางไปยังเนื้อหาของ URL นั้น เนื้อหาของ DOM จะไม่เปลี่ยนแปลง และจะไม่มีการสร้างอินสแตนซ์ใหม่ของ Window
, Document
ฯลฯ
fromURL()
นอกเหนือจากตัวสร้าง JSDOM
แล้ว jsdom ยังจัดเตรียมวิธีการส่งคืนโรงงานตามสัญญาสำหรับการสร้าง jsdom จาก URL:
JSDOM.fromURL("https://example.com/", options).then(dom => { console.log(dom.serialize());});
สัญญาที่ส่งคืนจะปฏิบัติตามอินสแตนซ์ JSDOM
หาก URL ถูกต้องและคำขอสำเร็จ การเปลี่ยนเส้นทางใด ๆ จะถูกติดตามไปยังจุดหมายปลายทางสูงสุด
ตัวเลือกที่มีให้กับ fromURL()
นั้นคล้ายคลึงกับตัวเลือกที่มีให้กับตัวสร้าง JSDOM
โดยมีข้อจำกัดและผลที่ตามมาเพิ่มเติมดังต่อไปนี้:
ไม่สามารถระบุตัวเลือก url
และ contentType
ได้
ตัวเลือก referrer
ถูกใช้เป็นส่วนหัวคำขอ Referer
HTTP ของคำขอเริ่มต้น
ตัวเลือก resources
ยังส่งผลต่อคำขอเริ่มต้นด้วย สิ่งนี้มีประโยชน์หากคุณต้องการกำหนดค่าพร็อกซี เช่น (ดูด้านบน)
URL, ประเภทเนื้อหา และผู้อ้างอิงที่เป็นผลลัพธ์ของ jsdom จะถูกกำหนดจากการตอบกลับ
คุกกี้ใดๆ ที่ตั้งค่าผ่านส่วนหัวการตอบสนอง HTTP Set-Cookie
จะถูกจัดเก็บไว้ในโถคุกกี้ของ jsdom ในทำนองเดียวกัน คุกกี้ใดๆ ที่อยู่ในโถคุกกี้ที่ให้มาจะถูกส่งเป็นส่วนหัวคำขอ Cookie
HTTP
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"
ตัวเลือก contentType
จะมีค่าเริ่มต้นเป็น "application/xhtml+xml"
หากชื่อไฟล์ที่กำหนดลงท้ายด้วย .xht
, .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
ขององค์ประกอบจะมีคุณสมบัติ defaultView
ที่เป็นค่าว่าง ทรัพยากรจะไม่โหลด ฯลฯ
การเรียกใช้แฟกทอรี fragment()
ทั้งหมดส่งผลให้ DocumentFragment
แชร์ Document
ของเจ้าของเทมเพลตคนเดียวกัน ซึ่งช่วยให้สามารถเรียกไปยัง fragment()
ได้หลายครั้งโดยไม่มีค่าใช้จ่ายเพิ่มเติม แต่ก็หมายความว่าการเรียกไปยัง fragment()
ไม่สามารถปรับแต่งด้วยตัวเลือกใด ๆ ได้
โปรดทราบว่าการทำให้เป็นอนุกรมนั้นไม่ใช่เรื่องง่ายด้วย DocumentFragment
เหมือนกับที่ทำกับออบเจ็กต์ JSDOM
แบบเต็ม หากคุณต้องการทำให้ DOM ของคุณเป็นอนุกรม คุณควรใช้ตัวสร้าง JSDOM
โดยตรงมากขึ้น แต่สำหรับกรณีพิเศษของแฟรกเมนต์ที่มีองค์ประกอบเดียว มันค่อนข้างง่ายที่จะทำด้วยวิธีปกติ:
const frag = JSDOM.fragment(`<p>สวัสดี</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
หรือประเภทข้อมูลไบนารี JavaScript มาตรฐาน เช่น ArrayBuffer
, Uint8Array
, DataView
เป็นต้น เมื่อดำเนินการเสร็จแล้ว jsdom จะดมกลิ่น การเข้ารหัสจากไบต์ที่ให้มา โดยการสแกนหาแท็ก <meta charset>
เช่นเดียวกับที่เบราว์เซอร์ทำ
หากตัวเลือก contentType
ที่ให้มามีพารามิเตอร์ charset
การเข้ารหัสนั้นจะแทนที่การเข้ารหัสแบบดมกลิ่น เว้นแต่จะมี BOM แบบ UTF-8 หรือ UTF-16 ซึ่งในกรณีนี้จะมีลำดับความสำคัญเหนือกว่า (ย้ำอีกครั้งว่านี่ก็เหมือนกับเบราว์เซอร์)
การดมการเข้ารหัสนี้ยังใช้กับ JSDOM.fromFile()
และ JSDOM.fromURL()
ในกรณีหลังนี้ ส่วนหัว Content-Type
ใดๆ ที่ส่งไปพร้อมกับการตอบกลับจะได้รับการจัดลำดับความสำคัญ ในลักษณะเดียวกับตัวเลือก contentType
ของ Constructor
โปรดทราบว่าในหลายกรณี การจัดหาไบต์ในลักษณะนี้อาจดีกว่าการจัดหาสตริง ตัวอย่างเช่น หากคุณพยายามใช้ API buffer.toString("utf-8")
ของ Node.js 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("พร้อมที่จะม้วน!");};
<!-- ภายใน HTML ที่คุณระบุให้กับ jsdom --><script>requirejs(["entry-module"], () => { window.onModulesLoaded();});</script>
หากคุณไม่ได้ควบคุมเพจ คุณสามารถลองใช้วิธีแก้ไขปัญหาชั่วคราว เช่น การโพลว่ามีองค์ประกอบใดอยู่บ้าง
สำหรับรายละเอียดเพิ่มเติม ดูการสนทนาใน #640 โดยเฉพาะความคิดเห็นที่ลึกซึ้งของ @matthewkastor
แม้ว่าเราจะสนุกกับการเพิ่มคุณสมบัติใหม่ให้กับ jsdom และคอยอัปเดตข้อกำหนดเว็บล่าสุดอยู่เสมอ แต่ก็มี API ที่ขาดหายไปจำนวนมาก โปรดแจ้งปัญหาหากขาดหายไป แต่เราเป็นทีมขนาดเล็กและมีงานยุ่ง ดังนั้นคำขอดึงอาจทำงานได้ดียิ่งขึ้น
คุณสมบัติบางอย่างของ jsdom นั้นมาจากการอ้างอิงของเรา เอกสารประกอบที่โดดเด่นในเรื่องนั้นรวมถึงรายการตัวเลือก CSS ที่รองรับสำหรับกลไกตัวเลือก CSS ของเรา nwsapi
นอกเหนือจากคุณสมบัติที่เรายังไม่เคยมี ยังมีคุณสมบัติหลักสองประการที่ปัจจุบันอยู่นอกขอบเขตของ jsdom เหล่านี้คือ:
การนำทาง : ความสามารถในการเปลี่ยนออบเจ็กต์ส่วนกลางและออบเจ็กต์อื่น ๆ ทั้งหมด เมื่อคลิกลิงก์หรือกำหนดตำแหน่ง location.href
หรือที่คล้ายกัน
เค้าโครง : ความสามารถในการคำนวณว่าองค์ประกอบต่างๆ จะถูกจัดวางในตำแหน่งใดซึ่งเป็นผลมาจาก CSS ซึ่งส่งผลต่อวิธีการเช่น getBoundingClientRects()
หรือคุณสมบัติเช่น offsetTop
ปัจจุบัน jsdom มีพฤติกรรมจำลองสำหรับบางแง่มุมของฟีเจอร์เหล่านี้ เช่น การส่ง "jsdomError"
ที่ "ไม่ได้ใช้งาน" ไปยังคอนโซลเสมือนเพื่อการนำทาง หรือการส่งคืนค่าศูนย์สำหรับคุณสมบัติที่เกี่ยวข้องกับโครงร่างจำนวนมาก บ่อยครั้งที่คุณสามารถแก้ไขข้อจำกัดเหล่านี้ในโค้ดของคุณได้ เช่น โดยการสร้างอินสแตนซ์ JSDOM
ใหม่สำหรับแต่ละเพจที่คุณ "นำทาง" ไปในระหว่างการรวบรวมข้อมูล หรือใช้ Object.defineProperty()
เพื่อเปลี่ยนสิ่งที่ได้รับและเมธอดที่เกี่ยวข้องกับโครงร่างต่างๆ ที่ส่งคืน
โปรดทราบว่าเครื่องมืออื่นๆ ในพื้นที่เดียวกัน เช่น PhantomJS รองรับคุณสมบัติเหล่านี้ บนวิกิ เรามีบทความที่สมบูรณ์ยิ่งขึ้นเกี่ยวกับ jsdom กับ PhantomJS
jsdom เป็นโครงการที่ขับเคลื่อนโดยชุมชนซึ่งดูแลโดยทีมอาสาสมัคร คุณสามารถสนับสนุน jsdom ได้โดย:
รับการสนับสนุนอย่างมืออาชีพสำหรับ jsdom โดยเป็นส่วนหนึ่งของการสมัครสมาชิก Tidelift Tidelift ช่วยทำให้โอเพ่นซอร์สมีความยั่งยืนสำหรับเรา ในขณะเดียวกันก็ให้การรับประกันแก่ทีมในด้านการบำรุงรักษา การออกใบอนุญาต และความปลอดภัย
มีส่วนร่วมกับโครงการโดยตรง
หากคุณต้องการความช่วยเหลือเกี่ยวกับ jsdom โปรดอย่าลังเลที่จะใช้สถานที่ต่อไปนี้:
รายชื่อผู้รับจดหมาย (ดีที่สุดสำหรับคำถาม "ฉันจะทำอย่างไร")
ตัวติดตามปัญหา (ดีที่สุดสำหรับรายงานข้อผิดพลาด)
ห้องเมทริกซ์: #jsdom:matrix.org