Shadow DOM v1 - คอมโพเนนต์เว็บในตัว

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

ดูแหล่งที่มาใน 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'}:

  1. ความรู้สึกของการรักษาความปลอดภัยที่ประดิษฐ์ขึ้นมา ไม่มีสิ่งใดที่จะหยุดผู้โจมตีได้ การปล้น Element.prototype.attachShadow

  2. โหมดปิดป้องกันไม่ให้โค้ดองค์ประกอบที่กำหนดเองของคุณเข้าถึงโค้ดของตัวเอง 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');
        }
        ...
    });
    
  3. โหมดปิดทำให้คอมโพเนนต์มีความยืดหยุ่นน้อยลงสำหรับผู้ใช้ปลายทาง ขณะที่คุณ สร้างคอมโพเนนต์ของเว็บ อาจมีบางครั้งที่คุณลืมเพิ่ม ตัวเลือกการกำหนดค่า กรณีการใช้งานที่ผู้ใช้ต้องการ ทั่วไป เช่น ลืมใส่ฮุกการจัดรูปแบบที่เพียงพอสำหรับโหนดภายใน ผู้ใช้จะไม่สามารถลบล้างค่าเริ่มต้นและปรับเปลี่ยนได้เมื่อใช้โหมดปิด รูปแบบ การเข้าถึงภายในของคอมโพเนนต์ได้มีประโยชน์อย่างยิ่ง สุดท้าย ผู้ใช้จะแยกคอมโพเนนต์ ค้นหาคอมโพเนนต์อื่น หรือสร้าง ของตัวเองหากไม่ได้ทำตามสิ่งที่พวกเขาต้องการ :(

การทำงานกับสล็อตใน 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]
&lt;my-component&gt;&lt;/my-component&gt; slot.assignedNodes(); []
&lt;my-component&gt;&lt;/my-component&gt; 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>

ผลลัพธ์

delegatesFocus: พฤติกรรมที่แท้จริง

ด้านบนคือผลลัพธ์เมื่อ <x-focus> โฟกัสอยู่ (การคลิกของผู้ใช้, แท็บ, focus() ฯลฯ) "ข้อความ Shadow DOM ที่คลิกได้" หรือแท็กภายใน <input> โฟกัสอยู่ (รวมถึง autofocus)

ถ้าคุณตั้งค่า delegatesFocus: false คุณจะเห็นข้อมูลต่อไปนี้แทน

วันที่ delegatesFocus: เท็จและอินพุตภายในจะโฟกัสอยู่
delegatesFocus: false และ <input> ภายในโฟกัสอยู่
delegatesFocus: เท็จและ x-Focus
    ได้รับโฟกัส (เช่น มี Tabindex=&#39;0&#39;)
delegatesFocus: false และ <x-focus> ได้โฟกัส (เช่น มี tabindex="0")
delegatesFocus: เท็จ และ &quot;ข้อความ Shadow DOM แบบคลิกได้&quot; เท่ากับ
    (หรือมีการคลิกพื้นที่ว่างอื่นๆ ภายใน Shadow DOM ขององค์ประกอบ)
delegatesFocus: false และ "ข้อความ Shadow DOM ที่คลิกได้" เท่ากับ (หรือมีการคลิกพื้นที่ว่างอื่นๆ ภายใน Shadow DOM ขององค์ประกอบ)

เคล็ดลับและคำแนะนำ

ในช่วงหลายปีที่ผ่านมาฉันได้เรียนรู้เกี่ยวกับการเขียนคอมโพเนนต์ของเว็บไปบ้าง การตีความ คิดว่าเคล็ดลับเหล่านี้อาจเป็นประโยชน์ในการเขียนคอมโพเนนต์และ การแก้ไขข้อบกพร่องของ 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 ได้ไหม

ใช้ Polyfill นะ ดูการรองรับเบราว์เซอร์

Shadow DOM มีฟีเจอร์การรักษาความปลอดภัยใดบ้าง

Shadow DOM ไม่ใช่ฟีเจอร์ด้านความปลอดภัย เพราะเป็นเครื่องมือขนาดเล็กสำหรับการกำหนดขอบเขต CSS และซ่อน DOM Tree ในคอมโพเนนต์ ถ้าต้องการขอบเขตความปลอดภัยที่แท้จริง ใช้ <iframe>

คอมโพเนนต์เว็บต้องใช้ Shadow DOM ไหม

ไม่ คุณไม่จำเป็นต้องสร้างคอมโพเนนต์เว็บที่ใช้ Shadow DOM อย่างไรก็ตาม การเขียนองค์ประกอบที่กำหนดเองที่ใช้ Shadow DOM หมายความว่า ใช้ประโยชน์จากฟีเจอร์ต่างๆ เช่น การกำหนดขอบเขต CSS, การห่อคลุม DOM และการจัดองค์ประกอบ

รากแสงเงาเปิดและปิดแตกต่างกันอย่างไร

ดูรากแสงเงาที่ปิดแล้ว