Shadow DOM ช่วยให้นักพัฒนาเว็บสร้าง DOM และ CSS แบบแยกช่องสำหรับคอมโพเนนต์ของเว็บได้
สรุป
Shadow DOM จะลบความเปราะบางของการสร้างเว็บแอป ความเปราะบาง
มาจาก HTML, CSS และ JS ทั่วไป ตลอดหลายปีที่ผ่านมา
คิดค้นจำนวนมากเกินไป
จาก
เครื่องมือ
เพื่อหลีกเลี่ยงปัญหา ตัวอย่างเช่น เมื่อคุณใช้รหัส/คลาส HTML ใหม่
ยังไม่มีข้อมูลใดๆ ที่บอกว่าจะขัดแย้งกับชื่อที่มีอยู่ซึ่งหน้าเว็บใช้อยู่ไหม
ข้อบกพร่องเล็กๆ น้อยๆ กำลังเพิ่มขึ้น
ความเฉพาะเจาะจงของ CSS กลายเป็นปัญหาใหญ่ (!important
ทั้งหมด!), สไตล์
ตัวเลือกเพิ่มขึ้นจากการควบคุม
ประสิทธิภาพอาจลดลงได้ รายการ
ต่อไป
Shadow DOM แก้ไข CSS และ DOM แนะนํารูปแบบที่กําหนดขอบเขตสําหรับเว็บ ที่มีการจัดการครบวงจรได้เลย หากไม่มีเครื่องมือหรือแบบแผนการตั้งชื่อ คุณสามารถรวม CSS กับ มาร์กอัป ซ่อนรายละเอียดการใช้งาน และบทความฉบับสมบูรณ์ของผู้เขียน คอมโพเนนต์ใน JavaScript แบบวานิลลา
บทนำ
Shadow DOM คือ 1 ใน 3 มาตรฐานคอมโพเนนต์เว็บคอมโพเนนต์ต่อไปนี้ เทมเพลต HTML, Shadow DOM และ องค์ประกอบที่กำหนดเอง การนำเข้า HTML เคยเป็นส่วนหนึ่งของรายการ แต่ตอนนี้ เลิกใช้งานแล้ว
คุณไม่จำเป็นต้องเขียนคอมโพเนนต์เว็บที่ใช้ Shadow DOM แต่เมื่อคุณทำเช่นนั้น คุณใช้ประโยชน์จากเครื่องมือนี้ (ขอบเขต CSS, การห่อคลุม DOM, องค์ประกอบ) และสร้างเนื้อหาที่นำมาใช้ใหม่ได้ องค์ประกอบที่กำหนดเอง ซึ่งยืดหยุ่น กำหนดค่าได้มาก และนำมาใช้ใหม่ได้อย่างมาก หากกำหนดเอง คือวิธีสร้าง HTML ใหม่ (ด้วย JS API) Shadow DOM คือ วิธีระบุ HTML และ CSS API ทั้งสองนี้จะรวมเข้าด้วยกันเพื่อสร้างคอมโพเนนต์ ด้วย HTML, CSS และ JavaScript แบบในตัว
Shadow DOM ออกแบบมาเพื่อเป็นเครื่องมือในการสร้างแอปแบบใช้คอมโพเนนต์ ดังนั้น บริษัทนำวิธีแก้ปัญหาทั่วไปในการพัฒนาเว็บมาไว้ดังนี้
- DOM แบบแยก: DOM ของคอมโพเนนต์เป็นแบบในตัว (เช่น
document.querySelector()
จะไม่แสดงผลโหนดใน Shadow DOM ของคอมโพเนนต์) - CSS ที่กำหนดขอบเขต: CSS ที่กำหนดภายใน Shadow DOM จะกำหนดขอบเขตไว้ที่ กฎรูปแบบ ไม่รั่วไหลออกไป และสไตล์ของหน้าก็จะไม่มีส่วนเข้ามา
- การเรียบเรียง: ออกแบบ API ตามมาร์กอัปสำหรับคอมโพเนนต์ที่ใช้ประกาศ
- ทำให้ CSS ง่ายขึ้น - DOM ที่กำหนดขอบเขตทำให้คุณสามารถใช้ตัวเลือก CSS แบบง่ายได้ ชื่อคลาส/รหัสทั่วไป และไม่ต้องกังวลเรื่องความขัดแย้งในการตั้งชื่อ
- ประสิทธิภาพการทำงาน - ลองนึกถึงแอปที่เป็นส่วนต่างๆ ของ DOM แทนที่จะนึกถึงแอปขนาดใหญ่ (ทั่วโลก)
การสาธิต fancy-tabs
ตลอดทั้งบทความนี้ เราจะพูดถึงคอมโพเนนต์สาธิต (<fancy-tabs>
)
และอ้างอิงข้อมูลโค้ดจากโค้ด หากเบราว์เซอร์รองรับ API คุณสามารถ
จะดูการสาธิตการใช้งานจริงได้ที่ด้านล่าง หรือลองดูแหล่งที่มาแบบสมบูรณ์ใน GitHub
Shadow DOM คืออะไร
ความเป็นมาเกี่ยวกับ DOM
HTML เป็นพลังขับเคลื่อนเว็บเนื่องจากง่ายต่อการทำงาน เมื่อประกาศแท็ก 2-3 รายการ สามารถเขียนหน้าเว็บที่มีทั้งการนำเสนอและโครงสร้างในไม่กี่วินาที อย่างไรก็ตาม HTML เพียงอย่างเดียวไม่ได้มีประโยชน์ทั้งหมด มนุษย์สามารถเข้าใจข้อความได้ง่าย พื้นฐานในภาษาหนึ่ง แต่แมชชีนต้องการบางสิ่งมากกว่านี้ ป้อนออบเจ็กต์เอกสาร โมเดล หรือ DOM
เมื่อเบราว์เซอร์โหลดหน้าเว็บ จะมีการทำสิ่งที่น่าสนใจมากมาย หนึ่งใน นั่นคือการแปลง HTML ของผู้เขียนให้เป็นเอกสารที่เผยแพร่อยู่ โดยทั่วไป เพื่อให้เข้าใจโครงสร้างของหน้าเว็บ เบราว์เซอร์จะแยกวิเคราะห์ HTML (คงที่ สตริงข้อความ) ลงในโมเดลข้อมูล (ออบเจ็กต์/โหนด) เบราว์เซอร์จะเก็บรักษา ลำดับชั้นของ HTML โดยการสร้างโครงสร้างของโหนดเหล่านี้: DOM สิ่งที่น่าสนใจ เกี่ยวกับ DOM ก็คือการแสดงหน้าเว็บจริง ต่างจากภาพนิ่ง HTML ที่เราเขียน โหนดที่เบราว์เซอร์สร้างขึ้น ประกอบด้วย คุณสมบัติ เมธอด และ ของทั้งหมด... สามารถได้รับการจัดการโดยโปรแกรม นั่นคือเหตุผลที่เราสามารถสร้าง DOM องค์ประกอบโดยตรงโดยใช้ JavaScript ดังนี้
const header = document.createElement('header');
const h1 = document.createElement('h1');
h1.textContent = 'Hello DOM';
header.appendChild(h1);
document.body.appendChild(header);
จะสร้างมาร์กอัป HTML ต่อไปนี้
<body>
<header>
<h1>Hello DOM</h1>
</header>
</body>
ทุกอย่างจะเป็นระบบที่ดี จากนั้น Shadow DOM คืออะไร
DOM... ซ่อนไว้
Shadow DOM เป็นเพียง DOM ปกติซึ่งมีความแตกต่าง 2 อย่าง คือ 1) วิธีสร้าง/ใช้งาน และ
2) ลักษณะการทำงานเมื่อเทียบกับส่วนอื่นๆ ของหน้าเว็บ โดยปกติแล้ว คุณจะสร้าง DOM
แล้วนำไปเพิ่มเป็นลูกของอีกองค์ประกอบหนึ่ง ด้วย Shadow DOM
สร้างแผนผัง DOM ที่มีขอบเขตซึ่งแนบกับองค์ประกอบ แต่แยกออกจาก
เด็กๆ จริงๆ ต้นไม้ส่วนย่อยที่กำหนดขอบเขตนี้เรียกว่าเงาต้นไม้ องค์ประกอบ
ที่แนบก็คือโฮสต์เงา สิ่งที่คุณเพิ่มในส่วนมืดจะกลายเป็น
ภายในองค์ประกอบโฮสติ้ง รวมถึง <style>
นี่คือ Shadow DOM
ได้รับการกำหนดขอบเขตรูปแบบ CSS
กำลังสร้าง Shadow DOM
รากที่เงาคือส่วนย่อยของเอกสารที่จะแนบอยู่กับองค์ประกอบ “โฮสต์”
การติดรูทเงาคือวิธีที่องค์ประกอบได้รับ Shadow DOM ถึง
สร้าง Shadow DOM สำหรับองค์ประกอบ เรียก element.attachShadow()
:
const header = document.createElement('header');
const shadowRoot = header.attachShadow({mode: 'open'});
shadowRoot.innerHTML = '<h1>Hello Shadow DOM</h1>'; // Could also use appendChild().
// header.shadowRoot === shadowRoot
// shadowRoot.host === header
ฉันใช้ .innerHTML
เพื่อเติมรากของเงา แต่คุณสามารถใช้ DOM อื่นๆ ได้
API นี่คือเว็บ เรามีทางเลือก
ข้อกำหนดกำหนดรายการองค์ประกอบ ที่ไม่สามารถโฮสต์เงาต้นไม้ได้ องค์ประกอบนั้นๆ อาจเกิดจากหลายสาเหตุ ในรายการ:
- เบราว์เซอร์โฮสต์ Shadow DOM ภายในของตัวเองสำหรับองค์ประกอบนั้นๆ อยู่แล้ว
(
<textarea>
,<input>
) - องค์ประกอบจะโฮสต์ Shadow DOM (
<img>
) ไม่เหมาะสม
ตัวอย่างเช่น วิธีนี้ไม่ได้ผล
document.createElement('input').attachShadow({mode: 'open'});
// Error. `<input>` cannot host shadow dom.
การสร้าง Shadow DOM สำหรับองค์ประกอบที่กำหนดเอง
Shadow DOM มีประโยชน์อย่างยิ่งเมื่อสร้าง องค์ประกอบที่กำหนดเอง ใช้ Shadow DOM เพื่อจัดแบ่ง HTML, CSS และ JS ขององค์ประกอบ สร้าง "คอมโพเนนต์เว็บ"
ตัวอย่าง - องค์ประกอบที่กำหนดเองแนบ Shadow DOM กับตัวเอง การห่อหุ้ม DOM/CSS ของตน:
// Use custom elements API v1 to register a new HTML tag and define its JS behavior
// using an ES6 class. Every instance of <fancy-tab> will have this same prototype.
customElements.define('fancy-tabs', class extends HTMLElement {
constructor() {
super(); // always call super() first in the constructor.
// Attach a shadow root to <fancy-tabs>.
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `
<style>#tabs { ... }</style> <!-- styles are scoped to fancy-tabs! -->
<div id="tabs">...</div>
<div id="panels">...</div>
`;
}
...
});
มีสิ่งที่น่าสนใจบางอย่างเกิดขึ้นที่นี่ ตัวเลือกแรกคือ
องค์ประกอบที่กำหนดเองสร้าง Shadow DOM ของตัวเองเมื่ออินสแตนซ์ของ <fancy-tabs>
แล้ว โดยทำใน constructor()
ข้อ 2 เนื่องจากเรากำลังสร้าง
รากที่ซ้อนอยู่ กฎ CSS ภายใน <style>
จะกำหนดขอบเขตเป็น <fancy-tabs>
การเรียบเรียงและช่อง
การเรียบเรียงเป็นหนึ่งในคุณลักษณะที่มีคนรู้จักน้อยที่สุดของ Shadow DOM แต่ จึงเป็นสิ่งที่สำคัญที่สุด
ในโลกของการพัฒนาเว็บ องค์ประกอบคือวิธีที่เราสร้างแอป
ว่าไม่ใช้ HTML ไปเลย องค์ประกอบพื้นฐานต่างๆ (<div>
, <header>
,
<form>
, <input>
) มารวมกันเพื่อสร้างแอป แท็กเหล่านี้บางรายการยังใช้ได้
ระหว่างกัน องค์ประกอบคือเหตุผลที่องค์ประกอบเนทีฟอย่าง <select>
<details>
, <form>
และ <video>
มีความยืดหยุ่นมาก แต่ละแท็กเหล่านี้ยอมรับ
บาง HTML ของเด็ก
และทำสิ่งพิเศษกับเด็ก ตัวอย่างเช่น
<select>
รู้วิธีแสดงผล <option>
และ <optgroup>
ในเมนูแบบเลื่อนลงและ
วิดเจ็ตที่เลือกหลายรายการ องค์ประกอบ <details>
แสดงผล <summary>
เป็น
ลูกศรที่ขยายได้ แม้แต่ <video>
ก็อาจยังรู้วิธีจัดการกับเด็กบางคน ดังนี้
องค์ประกอบ <source>
จะไม่แสดงผล แต่ส่งผลต่อลักษณะการทำงานของวิดีโอ
มหัศจรรย์อะไรขนาดนี้!
คำศัพท์: Light DOM กับ Shadow DOM
องค์ประกอบ Shadow DOM มีพื้นฐานใหม่มากมายในเว็บ ที่กำลังพัฒนา ก่อนจะเริ่มวัชพืช เรามาสร้างมาตรฐานกันก่อน คำศัพท์เฉพาะทางก็คือเราพูดภาษาเดียวกัน
Light DOM
มาร์กอัปที่ผู้ใช้ของคอมโพเนนต์เขียน DOM นี้อยู่นอก Shadow DOM ของคอมโพเนนต์ เป็นองค์ประกอบย่อยจริงขององค์ประกอบ
<better-button>
<!-- the image and span are better-button's light DOM -->
<img src="gear.svg" slot="icon">
<span>Settings</span>
</better-button>
Shadow DOM
DOM ที่ผู้เขียนคอมโพเนนต์เขียน Shadow DOM อยู่ในคอมโพเนนต์และ กำหนดโครงสร้างภายใน, CSS ที่กำหนดขอบเขต และสรุปการนำไปใช้งาน รายละเอียด อีกทั้งยังระบุวิธีแสดงผลมาร์กอัปที่ผู้บริโภคเขียนได้ด้วย ของคอมโพเนนต์
#shadow-root
<style>...</style>
<slot name="icon"></slot>
<span id="wrapper">
<slot>Button</slot>
</span>
แผนผัง DOM แบบแยกเดี่ยว
ผลจากการที่เบราว์เซอร์กระจาย Light DOM ของผู้ใช้ไปยังเงาของคุณ DOM แสดงผลผลิตภัณฑ์ขั้นสุดท้าย ต้นไม้ที่ตัดเป็นท่อนคือสิ่งที่คุณเห็นในท้ายที่สุด ในเครื่องมือสำหรับนักพัฒนาเว็บและสิ่งที่แสดงในหน้าเว็บ
<better-button>
#shadow-root
<style>...</style>
<slot name="icon">
<img src="gear.svg" slot="icon">
</slot>
<span id="wrapper">
<slot>
<span>Settings</span>
</slot>
</span>
</better-button>
ช่อง <slot> องค์ประกอบ
Shadow DOM เขียนแผนผัง DOM ต่างๆ เข้าด้วยกันโดยใช้องค์ประกอบ <slot>
ช่องโฆษณาคือตัวยึดตำแหน่งภายในคอมโพเนนต์ที่ผู้ใช้สามารถเติมช่อง
มาร์กอัปของตนเอง การกำหนดช่องโฆษณาอย่างน้อย 1 ช่องเป็นการเชิญมาร์กอัปภายนอกให้แสดงผล
ใน Shadow DOM ของคอมโพเนนต์ โดยพื้นฐานแล้ว คุณกำลังพูดว่า "แสดงผล
มาวางตรงนี้"
อนุญาตให้องค์ประกอบ "ข้าม" ขอบเขต Shadow DOM เมื่อ <slot>
เชิญ
ให้พวกเขา องค์ประกอบเหล่านี้เรียกว่าโหนดแบบกระจาย โดยหลักการ
โหนดแบบกระจายอาจดูแปลกๆ สล็อตจะไม่ย้าย DOM พวกเขา
แสดงผลที่ตำแหน่งอื่นภายใน Shadow DOM
คอมโพเนนต์กำหนดสล็อต 0 หรือหลายรายการใน Shadow DOM ได้ ช่องเว้นว่างได้ หรือสร้างเนื้อหาสำรอง หากผู้ใช้ไม่ได้ระบุ Light DOM ช่องโฆษณาจะแสดงเนื้อหาสำรอง
<!-- Default slot. If there's more than one default slot, the first is used. -->
<slot></slot>
<slot>fallback content</slot> <!-- default slot with fallback content -->
<slot> <!-- default slot entire DOM tree as fallback -->
<h2>Title</h2>
<summary>Description text</summary>
</slot>
คุณยังสามารถสร้างช่องโฆษณาที่มีชื่อได้อีกด้วย ช่องโฆษณาที่มีชื่อคือช่องที่เจาะจงใน Shadow DOM ที่ผู้ใช้อ้างอิงโดยใช้ชื่อ
ตัวอย่าง - สล็อตใน Shadow DOM ของ <fancy-tabs>
#shadow-root
<div id="tabs">
<slot id="tabsSlot" name="title"></slot> <!-- named slot -->
</div>
<div id="panels">
<slot id="panelsSlot"></slot>
</div>
ผู้ใช้คอมโพเนนต์จะประกาศ <fancy-tabs>
ดังนี้
<fancy-tabs>
<button slot="title">Title</button>
<button slot="title" selected>Title 2</button>
<button slot="title">Title 3</button>
<section>content panel 1</section>
<section>content panel 2</section>
<section>content panel 3</section>
</fancy-tabs>
<!-- Using <h2>'s and changing the ordering would also work! -->
<fancy-tabs>
<h2 slot="title">Title</h2>
<section>content panel 1</section>
<h2 slot="title" selected>Title 2</h2>
<section>content panel 2</section>
<h2 slot="title">Title 3</h2>
<section>content panel 3</section>
</fancy-tabs>
และหากคุณสงสัย ต้นไม้ที่แบนนั้นมีลักษณะดังนี้
<fancy-tabs>
#shadow-root
<div id="tabs">
<slot id="tabsSlot" name="title">
<button slot="title">Title</button>
<button slot="title" selected>Title 2</button>
<button slot="title">Title 3</button>
</slot>
</div>
<div id="panels">
<slot id="panelsSlot">
<section>content panel 1</section>
<section>content panel 2</section>
<section>content panel 3</section>
</slot>
</div>
</fancy-tabs>
โปรดสังเกตว่าคอมโพเนนต์ของเราสามารถจัดการ การกำหนดค่าแบบต่างๆ แต่
แผนผัง DOM ที่แยกเป็นหลายรายการแล้วจะยังเหมือนเดิม เรายังสามารถเปลี่ยนจาก <button>
เป็น
<h2>
คอมโพเนนต์นี้สร้างขึ้นเพื่อจัดการกับเด็กประเภทต่างๆ...
อย่างที่ <select>
ทำ!
การจัดรูปแบบ
การจัดรูปแบบคอมโพเนนต์เว็บมีหลายตัวเลือก คอมโพเนนต์ที่ใช้เงา DOM สามารถจัดรูปแบบตามหน้าหลัก กำหนดสไตล์ของตัวเอง หรือใส่ hook (ใน รูปแบบของคุณสมบัติที่กำหนดเองของ CSS) เพื่อให้ผู้ใช้ลบล้างค่าเริ่มต้นได้
รูปแบบที่คอมโพเนนต์กำหนด
เจาะลึกฟีเจอร์ที่มีประโยชน์ที่สุดของ Shadow DOM คือ CSS ที่มีขอบเขต ซึ่งมีดังนี้
- ตัวเลือก CSS จากหน้าด้านนอกจะไม่มีผลกับคอมโพเนนต์
- สไตล์ที่กำหนดไว้ด้านในจะไม่มีรอยต่อ โดยกำหนดขอบเขตไว้ที่องค์ประกอบโฮสต์
ตัวเลือก CSS ที่ใช้ภายใน Shadow DOM จะมีผลกับคอมโพเนนต์ของคุณภายใน ใน ซึ่งหมายความว่าเราจะใช้ชื่อ รหัส/คลาสที่ใช้กันทั่วไปได้อีกครั้งโดยไม่ต้องเป็นกังวล เกี่ยวกับความขัดแย้งในส่วนอื่นๆ ของหน้าเว็บ แนวทางปฏิบัติแนะนำคือการใช้ตัวเลือก CSS ที่ง่ายขึ้น ภายใน Shadow DOM ทั้งยังมีผลดีต่อประสิทธิภาพ
ตัวอย่าง - รูปแบบที่กำหนดในรากมืดเป็นแบบเฉพาะที่
#shadow-root
<style>
#panels {
box-shadow: 0 2px 2px rgba(0, 0, 0, .3);
background: white;
...
}
#tabs {
display: inline-flex;
...
}
</style>
<div id="tabs">
...
</div>
<div id="panels">
...
</div>
นอกจากนี้สไตล์ชีตยังกำหนดขอบเขตเป็นแผนผังเงา:
#shadow-root
<link rel="stylesheet" href="styles.css">
<div id="tabs">
...
</div>
<div id="panels">
...
</div>
เคยสงสัยไหมว่าองค์ประกอบ <select>
แสดงผลวิดเจ็ตแบบเลือกหลายรายการอย่างไร (แทนที่จะเป็น
เมนูแบบเลื่อนลง) เมื่อคุณเพิ่มแอตทริบิวต์ multiple
ดังนี้
<select multiple>
<option>Do</option>
<option selected>Re</option>
<option>Mi</option>
<option>Fa</option>
<option>So</option>
</select>
<select>
สามารถจัดรูปแบบตัวเองให้แตกต่างออกไปตามแอตทริบิวต์ที่คุณ
ต้องประกาศอย่างไร คอมโพเนนต์เว็บก็จัดรูปแบบตัวเองได้เช่นกันโดยใช้ :host
ตัวเลือก
ตัวอย่าง - การจัดรูปแบบคอมโพเนนต์เอง
<style>
:host {
display: block; /* by default, custom elements are display: inline */
contain: content; /* CSS containment FTW. */
}
</style>
Gotcha หนึ่งที่มี :host
คือกฎในหน้าหลักมีความจำเพาะสูงกว่า
กว่า :host
กฎที่กำหนดไว้ในองค์ประกอบ นั่นคือรูปแบบภายนอกจะชนะ ช่วงเวลานี้
ช่วยให้ผู้ใช้ลบล้างการจัดรูปแบบระดับบนสุดได้จากภายนอก และ :host
ด้วย
ทำงานได้ในบริบทของรากเงาเท่านั้น คุณจึงไม่สามารถนำไปใช้นอก
Shadow DOM
รูปแบบที่ใช้งานได้ของ :host(<selector>)
จะช่วยให้คุณกำหนดเป้าหมายโฮสต์ได้ หาก
ตรงกับ <selector>
นี่เป็นวิธีที่ยอดเยี่ยมในการห่อหุ้มคอมโพเนนต์
พฤติกรรมที่ตอบสนองต่อการโต้ตอบของผู้ใช้หรือสถานะหรือจัดรูปแบบโหนดภายในตาม
ในโฮสต์
<style>
:host {
opacity: 0.4;
will-change: opacity;
transition: opacity 300ms ease-in-out;
}
:host(:hover) {
opacity: 1;
}
:host([disabled]) { /* style when host has disabled attribute. */
background: grey;
pointer-events: none;
opacity: 0.4;
}
:host(.blue) {
color: blue; /* color host when it has class="blue" */
}
:host(.pink) > #tabs {
color: pink; /* color internal #tabs node when host has class="pink". */
}
</style>
การจัดรูปแบบตามบริบท
:host-context(<selector>)
จะจับคู่คอมโพเนนต์ หากองค์ประกอบหรือองค์ประกอบระดับบน
ตรงกับ <selector>
โดยทั่วไปแล้ว การกำหนดธีมนี้จะอิงตามแท็กของคอมโพเนนต์
สภาพแวดล้อม ตัวอย่างเช่น มีคนมากมายเลือกให้มีแนวคิดโดยนำชั้นเรียนไปใช้กับ
<html>
หรือ <body>
:
<body class="darktheme">
<fancy-tabs>
...
</fancy-tabs>
</body>
:host-context(.darktheme)
จะจัดรูปแบบ <fancy-tabs>
เมื่อเป็นองค์ประกอบสืบทอด
จาก .darktheme
:
:host-context(.darktheme) {
color: white;
background: black;
}
:host-context()
อาจเป็นประโยชน์สำหรับการกำหนดธีม แต่วิธีที่ดีกว่าคือ
สร้างฮุกสไตล์โดยใช้คุณสมบัติที่กำหนดเองของ CSS
การจัดรูปแบบโหนดแบบกระจาย
::slotted(<compound-selector>)
ตรงกับโหนดที่กระจายไปยัง
<slot>
สมมติว่าเราสร้างคอมโพเนนต์ป้ายชื่อ ดังนี้
<name-badge>
<h2>Eric Bidelman</h2>
<span class="title">
Digital Jedi, <span class="company">Google</span>
</span>
</name-badge>
Shadow DOM ของคอมโพเนนต์สามารถจัดรูปแบบ <h2>
และ .title
ของผู้ใช้ได้:
<style>
::slotted(h2) {
margin: 0;
font-weight: 300;
color: red;
}
::slotted(.title) {
color: orange;
}
/* DOESN'T WORK (can only select top-level nodes).
::slotted(.company),
::slotted(.title .company) {
text-transform: uppercase;
}
*/
</style>
<slot></slot>
หากคุณจำได้ว่าก่อนหน้านี้ <slot>
จะไม่ย้าย Light DOM ของผู้ใช้ วันและเวลา
โหนดต่างๆ จะกระจายไปยัง <slot>
ซึ่ง <slot>
จะแสดง DOM แต่
ให้โหนดของคุณคงที่ รูปแบบที่ใช้ก่อนการเผยแพร่จะดำเนินการต่อ
หลังการจัดจำหน่าย แต่เมื่อมีการกระจาย Light DOM ก็จะสามารถ
สร้างสไตล์เพิ่มเติม (รูปแบบที่กำหนดโดย Shadow DOM)
อีกตัวอย่างหนึ่งที่ละเอียดยิ่งขึ้นจาก <fancy-tabs>
:
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `
<style>
#panels {
box-shadow: 0 2px 2px rgba(0, 0, 0, .3);
background: white;
border-radius: 3px;
padding: 16px;
height: 250px;
overflow: auto;
}
#tabs {
display: inline-flex;
-webkit-user-select: none;
user-select: none;
}
#tabsSlot::slotted(*) {
font: 400 16px/22px 'Roboto';
padding: 16px 8px;
...
}
#tabsSlot::slotted([aria-selected="true"]) {
font-weight: 600;
background: white;
box-shadow: none;
}
#panelsSlot::slotted([aria-hidden="true"]) {
display: none;
}
</style>
<div id="tabs">
<slot id="tabsSlot" name="title"></slot>
</div>
<div id="panels">
<slot id="panelsSlot"></slot>
</div>
`;
ในตัวอย่างนี้ มีช่อง 2 ช่อง ได้แก่ ช่องที่ตั้งชื่อสำหรับชื่อแท็บ และ
สำหรับเนื้อหาแผงแท็บ เมื่อผู้ใช้เลือกแท็บ เราจะทำการเลือกของตนเป็นตัวหนา
และแสดงแผงควบคุม โดยการเลือกโหนดแบบกระจายที่มี
selected
JS ขององค์ประกอบที่กำหนดเอง (ไม่ได้แสดงที่นี่) จะเพิ่มว่า
ในเวลาที่ถูกต้อง
การจัดรูปแบบคอมโพเนนต์จากภายนอก
การจัดรูปแบบคอมโพเนนต์จากภายนอกมี 2 วิธี ง่ายที่สุด วิธีก็คือใช้ชื่อแท็กเป็นตัวเลือก
fancy-tabs {
width: 500px;
color: red; /* Note: inheritable CSS properties pierce the shadow DOM boundary. */
}
fancy-tabs:hover {
box-shadow: 0 3px 3px #ccc;
}
สไตล์ภายนอกจะชนะรูปแบบที่กำหนดไว้ใน Shadow DOM เสมอ ตัวอย่างเช่น
หากผู้ใช้เขียนตัวเลือก fancy-tabs { width: 500px; }
ก็จะมีลำดับความสำคัญสูงกว่า
กฎของคอมโพเนนต์: :host { width: 650px;}
การจัดรูปแบบคอมโพเนนต์เองจะช่วยให้คุณได้ประโยชน์เท่านั้น แต่จะเกิดอะไรขึ้นหากคุณ ต้องการจัดรูปแบบภายในของคอมโพเนนต์ไหม จึงต้องมี CSS ที่กำหนดเอง พร็อพเพอร์ตี้
การสร้างฮุกสไตล์โดยใช้คุณสมบัติที่กำหนดเองของ CSS
ผู้ใช้ปรับเปลี่ยนรูปแบบภายในได้ หากผู้เขียนคอมโพเนนต์ให้ฮุกการจัดรูปแบบ
โดยใช้คุณสมบัติที่กำหนดเองของ CSS โดยหลักการแล้ว แนวคิดจะคล้ายกับ
<slot>
คุณสร้าง "ตัวยึดตำแหน่งรูปแบบ" ให้ผู้ใช้ลบล้างได้
ตัวอย่าง - <fancy-tabs>
อนุญาตให้ผู้ใช้ลบล้างสีพื้นหลังได้
<!-- main page -->
<style>
fancy-tabs {
margin-bottom: 32px;
--fancy-tabs-bg: black;
}
</style>
<fancy-tabs background>...</fancy-tabs>
ภายใน Shadow DOM:
:host([background]) {
background: var(--fancy-tabs-bg, #9E9E9E);
border-radius: 10px;
padding: 10px;
}
ในกรณีนี้ คอมโพเนนต์จะใช้ black
เป็นค่าพื้นหลังเนื่องจากแอตทริบิวต์
ผู้ใช้ระบุ มิเช่นนั้น ค่าเริ่มต้นจะเป็น #9E9E9E
หัวข้อขั้นสูง
การสร้างรากแบบปิด (ควรหลีกเลี่ยง)
มี Shadow DOM อีกเวอร์ชันชื่อว่า "Closed" เมื่อคุณสร้าง
โครงสร้างเงาแบบปิด เนื่องจาก JavaScript ภายนอกจะเข้าถึง DOM ภายในไม่ได้
ของคอมโพเนนต์ ซึ่งคล้ายกับวิธีการทำงานขององค์ประกอบเนทีฟ เช่น <video>
JavaScript ไม่สามารถเข้าถึง Shadow DOM ของ <video>
เนื่องจากเบราว์เซอร์
ใช้งานโดยใช้รากเงาในโหมดปิด
ตัวอย่าง - การสร้างแผนผังเงาแบบปิด
const div = document.createElement('div');
const shadowRoot = div.attachShadow({mode: 'closed'}); // close shadow tree
// div.shadowRoot === null
// shadowRoot.host === div
API อื่นๆ ยังได้รับผลกระทบจากโหมดปิดด้วย ดังนี้
Element.assignedSlot
/TextNode.assignedSlot
ส่งคืนnull
Event.composedPath()
สำหรับเหตุการณ์ที่เกี่ยวข้องกับองค์ประกอบภายในเงา DOM แสดงผล []
ฉันสรุปเหตุผลที่คุณไม่ควรสร้างองค์ประกอบเว็บด้วย
{mode: 'closed'}
:
ความรู้สึกของการรักษาความปลอดภัยที่ประดิษฐ์ขึ้นมา ไม่มีสิ่งใดที่จะหยุดผู้โจมตีได้ การปล้น
Element.prototype.attachShadow
โหมดปิดป้องกันไม่ให้โค้ดองค์ประกอบที่กำหนดเองของคุณเข้าถึงโค้ดของตัวเอง Shadow DOM นั่นคือไม่ผ่านค่ะ แต่คุณจะต้องซ่อนข้อมูลอ้างอิงไว้แทน ไว้ทีหลัง หากต้องการใช้อย่าง
querySelector()
อย่างสมบูรณ์ เอาชนะจุดประสงค์เดิมของโหมดปิดได้customElements.define('x-element', class extends HTMLElement { constructor() { super(); // always call super() first in the constructor. this._shadowRoot = this.attachShadow({mode: 'closed'}); this._shadowRoot.innerHTML = '<div class="wrapper"></div>'; } connectedCallback() { // When creating closed shadow trees, you'll need to stash the shadow root // for later if you want to use it again. Kinda pointless. const wrapper = this._shadowRoot.querySelector('.wrapper'); } ... });
โหมดปิดทำให้คอมโพเนนต์มีความยืดหยุ่นน้อยลงสำหรับผู้ใช้ปลายทาง ขณะที่คุณ สร้างคอมโพเนนต์ของเว็บ อาจมีบางครั้งที่คุณลืมเพิ่ม ตัวเลือกการกำหนดค่า กรณีการใช้งานที่ผู้ใช้ต้องการ ทั่วไป เช่น ลืมใส่ฮุกการจัดรูปแบบที่เพียงพอสำหรับโหนดภายใน ผู้ใช้จะไม่สามารถลบล้างค่าเริ่มต้นและปรับเปลี่ยนได้เมื่อใช้โหมดปิด รูปแบบ การเข้าถึงภายในของคอมโพเนนต์ได้มีประโยชน์อย่างยิ่ง สุดท้าย ผู้ใช้จะแยกคอมโพเนนต์ ค้นหาคอมโพเนนต์อื่น หรือสร้าง ของตัวเองหากไม่ได้ทำตามสิ่งที่พวกเขาต้องการ :(
การทำงานกับสล็อตใน JS
Shadow DOM API มียูทิลิตีสำหรับการทำงานกับสล็อตและการกระจาย ซึ่งมีประโยชน์มากเมื่อเขียนองค์ประกอบที่กำหนดเอง
เหตุการณ์ slotchange
เหตุการณ์ slotchange
เริ่มทำงานเมื่อโหนดแบบกระจายของช่องมีการเปลี่ยนแปลง สำหรับ
ตัวอย่างเช่น หากผู้ใช้เพิ่ม/นำรายการย่อยออกจาก Light DOM
const slot = this.shadowRoot.querySelector('#slot');
slot.addEventListener('slotchange', e => {
console.log('light dom children changed!');
});
ในการตรวจสอบการเปลี่ยนแปลงประเภทอื่นๆ ใน Light DOM คุณสามารถตั้งค่า
MutationObserver
ในตัวสร้างเอลิเมนต์
องค์ประกอบใดที่แสดงผลในช่องอยู่
บางครั้งการทราบว่าองค์ประกอบใดเชื่อมโยงกับช่องโฆษณาอาจเป็นประโยชน์ โทร
slot.assignedNodes()
เพื่อค้นหาองค์ประกอบที่ช่องโฆษณากำลังแสดงอยู่
ตัวเลือก {flatten: true}
จะแสดงผลเนื้อหาสำรองของช่องด้วย (หากไม่มีโหนด
ที่กำลังเผยแพร่อยู่)
ตัวอย่างเช่น สมมติว่า Shadow DOM ของคุณมีหน้าตาดังนี้
<slot><b>fallback content</b></slot>
การใช้งาน | โทร | ผลลัพธ์ |
---|---|---|
<my-component>ข้อความคอมโพเนนต์</my-component> | slot.assignedNodes(); |
[component text] |
<my-component></my-component> | slot.assignedNodes(); |
[] |
<my-component></my-component> | slot.assignedNodes({flatten: true}); |
[<b>fallback content</b>] |
องค์ประกอบที่กำหนดให้กับช่องใด
คุณสามารถตอบคำถามที่ย้อนกลับได้ element.assignedSlot
บอก
คุณกำหนดให้องค์ประกอบใดเป็นช่องคอมโพเนนต์
โมเดลเหตุการณ์ Shadow DOM
เมื่อเหตุการณ์ลูกโป่งแสดงขึ้นจาก Shadow DOM ระบบจะปรับเป้าหมายเพื่อรักษา การห่อหุ้มที่ Shadow DOM มีให้ กล่าวคือ กิจกรรมจะได้รับการกำหนดเป้าหมายใหม่เพื่อดู เช่น มาจากคอมโพเนนต์ ไม่ใช่องค์ประกอบภายใน Shadow DOM บางเหตุการณ์ไม่เผยแพร่ออกจาก Shadow DOM ด้วยซ้ำ
เหตุการณ์ที่เกิดขึ้นข้ามขอบเขตของเงา มีดังนี้
- กิจกรรมโฟกัส:
blur
,focus
,focusin
,focusout
- เหตุการณ์ของเมาส์:
click
,dblclick
,mousedown
,mouseenter
,mousemove
เป็นต้น - เหตุการณ์ของล้อ:
wheel
- เหตุการณ์อินพุต:
beforeinput
,input
- เหตุการณ์แป้นพิมพ์:
keydown
,keyup
- เหตุการณ์การเรียบเรียง:
compositionstart
,compositionupdate
,compositionend
- DragEvent:
dragstart
,drag
,dragend
,drop
ฯลฯ
เคล็ดลับ
หากเงาต้นไม้เปิดอยู่ การเรียกใช้ event.composedPath()
จะแสดงผลอาร์เรย์
ของโหนดที่เหตุการณ์เดินทางผ่าน
การใช้เหตุการณ์ที่กำหนดเอง
เหตุการณ์ DOM ที่กำหนดเองซึ่งเริ่มทำงานบนโหนดภายในใน Shadow Tree ไม่เริ่มทำงาน
ฟองอากาศออกจากขอบเขตเงา ยกเว้นกรณีที่มีการสร้างเหตุการณ์โดยใช้
แฟล็ก composed: true
รายการ:
// Inside <fancy-tab> custom element class definition:
selectTab() {
const tabs = this.shadowRoot.querySelector('#tabs');
tabs.dispatchEvent(new Event('tab-select', {bubbles: true, composed: true}));
}
หากเป็น composed: false
(ค่าเริ่มต้น) ผู้บริโภคจะฟังกิจกรรมไม่ได้
นอกรากเงาของคุณ
<fancy-tabs></fancy-tabs>
<script>
const tabs = document.querySelector('fancy-tabs');
tabs.addEventListener('tab-select', e => {
// won't fire if `tab-select` wasn't created with `composed: true`.
});
</script>
โฟกัสการจัดการ
หากคุณจำได้จากโมเดลเหตุการณ์ของ Shadow DOM แสดงว่าเหตุการณ์ที่เริ่มทำงาน
ภายใน Shadow DOM จะได้รับการปรับให้ดูเหมือนมาจากองค์ประกอบโฮสติ้ง
ตัวอย่างเช่น สมมติว่าคุณคลิก <input>
ภายในรากที่เป็นเงา
<x-focus>
#shadow-root
<input type="text" placeholder="Input inside shadow dom">
เหตุการณ์ focus
จะดูเหมือนว่ามาจาก <x-focus>
ไม่ใช่ <input>
ในทำนองเดียวกัน document.activeElement
จะมีค่าเป็น <x-focus>
หากรากเงา
สร้างด้วย mode:'open'
(ดูโหมดปิด) และคุณจะ
สามารถเข้าถึงโหนดภายในที่ได้โฟกัส:
document.activeElement.shadowRoot.activeElement // only works with open mode.
หากมี Shadow DOM ในหลายระดับ (เช่น องค์ประกอบที่กำหนดเองภายใน
องค์ประกอบที่กำหนดเองอีกรายการ) คุณจะต้องเจาะลึกลงไปในรากของเงาเพื่อ
ค้นหา activeElement
:
function deepActiveElement() {
let a = document.activeElement;
while (a && a.shadowRoot && a.shadowRoot.activeElement) {
a = a.shadowRoot.activeElement;
}
return a;
}
อีกตัวเลือกหนึ่งสำหรับการโฟกัสคือตัวเลือก delegatesFocus: true
ซึ่งจะขยาย
พฤติกรรมการโฟกัสขององค์ประกอบภายในแผนผังเงา
- หากคุณคลิกโหนดภายใน Shadow DOM และโหนดไม่ใช่พื้นที่ที่โฟกัสได้ ด้านแรกที่สามารถโฟกัสได้
- เมื่อโหนดภายใน Shadow DOM ได้รับโฟกัส
:focus
จะใช้กับโฮสต์ใน การเพิ่มไปยังองค์ประกอบที่โฟกัส
ตัวอย่าง - วิธีที่ delegatesFocus: true
เปลี่ยนพฤติกรรมการโฟกัส
<style>
:focus {
outline: 2px solid red;
}
</style>
<x-focus></x-focus>
<script>
customElements.define('x-focus', class extends HTMLElement {
constructor() {
super(); // always call super() first in the constructor.
const root = this.attachShadow({mode: 'open', delegatesFocus: true});
root.innerHTML = `
<style>
:host {
display: flex;
border: 1px dotted black;
padding: 16px;
}
:focus {
outline: 2px solid blue;
}
</style>
<div>Clickable Shadow DOM text</div>
<input type="text" placeholder="Input inside shadow dom">`;
// Know the focused element inside shadow DOM:
this.addEventListener('focus', function(e) {
console.log('Active element (inside shadow dom):',
this.shadowRoot.activeElement);
});
}
});
</script>
ผลลัพธ์
ด้านบนคือผลลัพธ์เมื่อ <x-focus>
โฟกัสอยู่ (การคลิกของผู้ใช้, แท็บ,
focus()
ฯลฯ) "ข้อความ Shadow DOM ที่คลิกได้" หรือแท็กภายใน
<input>
โฟกัสอยู่ (รวมถึง autofocus
)
ถ้าคุณตั้งค่า delegatesFocus: false
คุณจะเห็นข้อมูลต่อไปนี้แทน
เคล็ดลับและคำแนะนำ
ในช่วงหลายปีที่ผ่านมาฉันได้เรียนรู้เกี่ยวกับการเขียนคอมโพเนนต์ของเว็บไปบ้าง การตีความ คิดว่าเคล็ดลับเหล่านี้อาจเป็นประโยชน์ในการเขียนคอมโพเนนต์และ การแก้ไขข้อบกพร่องของ Shadow DOM
ใช้การควบคุม CSS
โดยทั่วไป เลย์เอาต์/รูปแบบ/สีของคอมโพเนนต์เว็บจะค่อนข้างทำงานได้ในตัวเอง ใช้
การควบคุม CSS ใน :host
สำหรับประสิทธิภาพ
ชนะ:
<style>
:host {
display: block;
contain: content; /* Boom. CSS containment FTW. */
}
</style>
กำลังรีเซ็ตรูปแบบที่รับช่วงได้
ใช้รูปแบบที่รับช่วงมา (background
, color
, font
, line-height
ฯลฯ) ต่อไปได้
เพื่อรับค่าใน Shadow DOM กล่าวคือ เจาะขอบเขตของ Shadow DOM โดย
"ค่าเริ่มต้น" หากต้องการเริ่มต้นด้วยแถบสเลทใหม่ ให้ใช้ all: initial;
เพื่อรีเซ็ต
รูปแบบที่สืบทอดได้ไปยังค่าเริ่มต้นเมื่อข้ามขอบเขตของเงา
<style>
div {
padding: 10px;
background: red;
font-size: 25px;
text-transform: uppercase;
color: white;
}
</style>
<div>
<p>I'm outside the element (big/white)</p>
<my-element>Light DOM content is also affected.</my-element>
<p>I'm outside the element (big/white)</p>
</div>
<script>
const el = document.querySelector('my-element');
el.attachShadow({mode: 'open'}).innerHTML = `
<style>
:host {
all: initial; /* 1st rule so subsequent properties are reset. */
display: block;
background: white;
}
</style>
<p>my-element: all CSS properties are reset to their
initial value using <code>all: initial</code>.</p>
<slot></slot>
`;
</script>
การค้นหาองค์ประกอบที่กำหนดเองทั้งหมดที่หน้าเว็บใช้
บางครั้งการค้นหาองค์ประกอบที่กำหนดเองที่ใช้ในหน้าเว็บก็มีประโยชน์ วิธีการก็คือ จำเป็นต้องข้ามผ่าน Shadow DOM ขององค์ประกอบทั้งหมดที่ใช้ในหน้า
const allCustomElements = [];
function isCustomElement(el) {
const isAttr = el.getAttribute('is');
// Check for <super-button> and <button is="super-button">.
return el.localName.includes('-') || isAttr && isAttr.includes('-');
}
function findAllCustomElements(nodes) {
for (let i = 0, el; el = nodes[i]; ++i) {
if (isCustomElement(el)) {
allCustomElements.push(el);
}
// If the element has shadow DOM, dig deeper.
if (el.shadowRoot) {
findAllCustomElements(el.shadowRoot.querySelectorAll('*'));
}
}
}
findAllCustomElements(document.querySelectorAll('*'));
การสร้างองค์ประกอบจาก <template>
แทนที่จะสร้างรูทเงาโดยใช้ .innerHTML
เราสามารถใช้การประกาศ
<template>
เทมเพลตคือตัวยึดตำแหน่งที่เหมาะสำหรับการประกาศโครงสร้างของ
คอมโพเนนต์เว็บ
ดูตัวอย่างใน "องค์ประกอบที่กำหนดเอง: การสร้างคอมโพเนนต์เว็บที่นำมาใช้ใหม่ได้"
ประวัติศาสตร์และ การสนับสนุนเบราว์เซอร์
หากคุณติดตามคอมโพเนนต์ของเว็บในช่วง 2-3 ปีที่ผ่านมา คุณจะ
ทราบว่า Chrome 35+/Opera ได้รับ Shadow DOM เวอร์ชันเก่าสำหรับ
สักครั้ง Blink จะยังรองรับทั้ง 2 เวอร์ชันพร้อมกันสำหรับบางเครื่อง
ข้อกำหนด v0 มีวิธีอื่นในการสร้างรากเงา
(element.createShadowRoot
แทน element.attachShadow
ของ v1) การเรียกใช้
เมธอดเก่าจะยังคงสร้างรากเงาด้วยความหมาย v0 ดังนั้น v0 ที่มีอยู่
โค้ดจะไม่เสียหาย
ถ้าคุณสนใจข้อกำหนด v0 เก่า ลองดูที่ html5rocks บทความ: 1 2, 3. และยังมีการเปรียบเทียบที่ยอดเยี่ยมของ ความแตกต่างระหว่าง Shadow DOM v0 และ v1
การสนับสนุนเบราว์เซอร์
Shadow DOM v1 จัดส่งใน Chrome 53 (สถานะ) แล้ว Opera 40, Safari 10 และ Firefox 63 ขอบ ได้เริ่มการพัฒนา
ในฟีเจอร์การตรวจหา Shadow DOM ให้ตรวจสอบการมีอยู่ของ attachShadow
:
const supportsShadowDOMV1 = !!HTMLElement.prototype.attachShadow;
ใยโพลีเอสเตอร์
จนกว่าจะมีการรองรับเบราว์เซอร์ในวงกว้าง แพลตฟอร์ม และ shadycss Polyfills จะให้คุณ v1 Shady DOM จะเลียนแบบขอบเขต DOM ของ Shadow DOM และ shadycss polyfill พร็อพเพอร์ตี้ที่กำหนดเองของ CSS และสไตล์ที่กำหนดขอบเขตของ API แบบเนทีฟ
ติดตั้ง Polyfills ด้วยคำสั่งต่อไปนี้
bower install --save webcomponents/shadydom
bower install --save webcomponents/shadycss
ใช้ Polyfills:
function loadScript(src) {
return new Promise(function(resolve, reject) {
const script = document.createElement('script');
script.async = true;
script.src = src;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
// Lazy load the polyfill if necessary.
if (!supportsShadowDOMV1) {
loadScript('/bower_components/shadydom/shadydom.min.js')
.then(e => loadScript('/bower_components/shadycss/shadycss.min.js'))
.then(e => {
// Polyfills loaded.
});
} else {
// Native shadow dom v1 support. Go to go!
}
โปรดดู https://github.com/webcomponents/shadycss#usage เพื่อดูวิธีส่อง/จำกัดสไตล์
บทสรุป
นี่เป็นครั้งแรกที่เรามี API แบบพื้นฐานที่กำหนดขอบเขต CSS อย่างเหมาะสม
กำหนดขอบเขต DOM และมีองค์ประกอบที่แท้จริง รวมกับ API คอมโพเนนต์เว็บอื่นๆ
เช่น องค์ประกอบที่กำหนดเอง Shadow DOM ให้วิธีการเขียนที่ห่อหุ้มอย่างแท้จริง
คอมโพเนนต์ที่ไม่ถูกแฮกหรือใช้กระเป๋าเดินทางรุ่นเก่า เช่น <iframe>
อย่าทำให้ฉันเข้าใจผิด Shadow DOM เป็นสัตว์ที่ซับซ้อนอย่างแน่นอน แต่มันคือสัตว์ที่จริง คุ้มค่ากับการเรียนรู้ ลองใช้เวลาสักครู่ เรียนรู้และถามคำถาม
อ่านเพิ่มเติม
- ความแตกต่างระหว่าง Shadow DOM v1 และ v0
- "ขอแนะนำ Shadow DOM API ที่อิงตามช่อง" จากบล็อก WebKit
- เว็บคอมโพเนนต์และอนาคตของ CSS แบบโมดูล โดย Philip Walton
- "องค์ประกอบที่กำหนดเอง: การสร้างคอมโพเนนต์เว็บที่นำมาใช้ใหม่ได้" จาก WebFundamentals ของ Google
- ข้อกำหนดของ Shadow DOM v1
- ข้อกำหนดขององค์ประกอบที่กำหนดเอง v1
คำถามที่พบบ่อย
วันนี้ฉันจะใช้ Shadow DOM v1 ได้ไหม
ใช้ Polyfill นะ ดูการรองรับเบราว์เซอร์
Shadow DOM มีฟีเจอร์การรักษาความปลอดภัยใดบ้าง
Shadow DOM ไม่ใช่ฟีเจอร์ด้านความปลอดภัย เพราะเป็นเครื่องมือขนาดเล็กสำหรับการกำหนดขอบเขต CSS
และซ่อน DOM Tree ในคอมโพเนนต์ ถ้าต้องการขอบเขตความปลอดภัยที่แท้จริง
ใช้ <iframe>
คอมโพเนนต์เว็บต้องใช้ Shadow DOM ไหม
ไม่ คุณไม่จำเป็นต้องสร้างคอมโพเนนต์เว็บที่ใช้ Shadow DOM อย่างไรก็ตาม การเขียนองค์ประกอบที่กำหนดเองที่ใช้ Shadow DOM หมายความว่า ใช้ประโยชน์จากฟีเจอร์ต่างๆ เช่น การกำหนดขอบเขต CSS, การห่อคลุม DOM และการจัดองค์ประกอบ
รากแสงเงาเปิดและปิดแตกต่างกันอย่างไร