Shadow DOM v1 - सेल्फ़-कंटेन्ड वेब कॉम्पोनेंट

शैडो DOM की मदद से वेब डेवलपर, वेब कॉम्पोनेंट के लिए अलग-अलग कैटगरी में बांटा गया DOM और सीएसएस बना सकते हैं

खास जानकारी

Shadow DOM वेब ऐप्लिकेशन बनाने की साधारणता को हटा देता है. यह झलक, एचटीएमएल, सीएसएस, और JS की ग्लोबल प्रकृति से आती है. पिछले कुछ सालों में हमने इन समस्याओं से बचने के लिए tools की बहुत ज़्यादा संख्या ईजाद की है. उदाहरण के लिए, जब किसी नए एचटीएमएल आईडी/क्लास का इस्तेमाल किया जाता है, तो इस बात की कोई जानकारी नहीं दी जाती है कि इसका असर, पेज पर इस्तेमाल किए गए मौजूदा नाम से होगा या नहीं. छोटे-छोटे बग आते हैं, सीएसएस की खासियत एक बड़ी समस्या (!important यह सब!) बन जाती है, स्टाइल चुनने वाले टूल का कंट्रोल खत्म हो जाता है और परफ़ॉर्मेंस पर बुरा असर पड़ सकता है. सूची आगे हो जाती है.

शैडो DOM ने सीएसएस और डीओएम को ठीक किया है. यह वेब प्लैटफ़ॉर्म पर स्कोप वाली स्टाइल के बारे में बताता है. टूल या नाम रखने के तौर-तरीकों के बिना, सीएसएस को मार्कअप के साथ बंडल किया जा सकता है, लागू करने की जानकारी छिपाई जा सकती है, और वनीला JavaScript में ऑथर से भरपूर कॉम्पोनेंट लिए जा सकते हैं.

शुरुआती जानकारी

शैडो DOM तीन वेब कॉम्पोनेंट स्टैंडर्ड में से एक है: एचटीएमएल टेंप्लेट, Shadow DOM, और कस्टम एलिमेंट. एचटीएमएल इंपोर्ट को इस सूची का हिस्सा माना जाता था, लेकिन अब इन्हें अब इस्तेमाल में नहीं है माना जाता है.

आपको शैडो DOM का इस्तेमाल करने वाले वेब कॉम्पोनेंट लिखने की ज़रूरत नहीं है. हालांकि, ऐसा करने पर आपको इसके फ़ायदों (सीएसएस स्कोपिंग, डीओएम एनकैप्सुलेशन, कंपोज़िशन) का फ़ायदा मिलता है. साथ ही, फिर से इस्तेमाल किए जा सकने वाले कस्टम एलिमेंट बनाए जाते हैं. ये एलिमेंट ज़रूरत के हिसाब से काम करते हैं, इन्हें ज़रूरत के हिसाब से कॉन्फ़िगर किया जा सकता है, और इन्हें दोबारा इस्तेमाल किया जा सकता है. अगर कस्टम एलिमेंट का इस्तेमाल करके नया एचटीएमएल (JS API) बनाया जा सकता है, तो शैडो DOM का इस्तेमाल करके एचटीएमएल और सीएसएस का इस्तेमाल किया जा सकता है. सेल्फ़-कंटेन्ड एचटीएमएल, सीएसएस, और JavaScript के साथ कॉम्पोनेंट बनाने के लिए, दोनों एपीआई का इस्तेमाल किया जाता है.

Shadow DOM को कॉम्पोनेंट पर आधारित ऐप्लिकेशन बनाने के टूल के तौर पर डिज़ाइन किया गया है. इसलिए, यह वेब डेवलपमेंट में आने वाली आम समस्याओं का समाधान बताता है:

  • आइसोलेटेड डीओएम: कॉम्पोनेंट का डीओएम खुद में पूरा होता है (उदाहरण के लिए, document.querySelector() कॉम्पोनेंट के शैडो डीओएम में नोड नहीं दिखाएगा).
  • स्कोप वाला सीएसएस: शैडो DOM में तय किया गया सीएसएस, उसके स्कोप में होता है. स्टाइल से जुड़े नियम लीक नहीं होते हैं और पेज स्टाइल से कोई फ़र्क़ नहीं पड़ता है.
  • कंपोज़िशन: अपने कॉम्पोनेंट के लिए, जानकारी देने वाला, मार्कअप पर आधारित एपीआई डिज़ाइन करें.
  • सीएसएस को आसान बनाता है - स्कोप वाले डीओएम का मतलब है कि आसान सीएसएस सिलेक्टर, ज़्यादा सामान्य आईडी/क्लास नामों का इस्तेमाल किया जा सकता है. साथ ही, एक ही नाम से अलग-अलग नाम होने की चिंता नहीं की जा सकती.
  • प्रोडक्टिविटी - ऐप्लिकेशन को बड़े (ग्लोबल) पेज के बजाय, डीओएम के हिसाब से अलग-अलग कैटगरी में रखें.

fancy-tabs का डेमो

इस पूरे लेख में, हम डेमो कॉम्पोनेंट (<fancy-tabs>) के बारे में बात करेंगे और इससे मिले कोड स्निपेट के बारे में बात करेंगे. अगर आपके ब्राउज़र पर एपीआई काम करते हैं, तो आपको नीचे उसका लाइव डेमो दिखेगा. अगर ऐसा नहीं है, तो GitHub पर पूरा सोर्स देखें.

GitHub पर सोर्स देखें

शैडो DOM क्या है?

DOM पर बैकग्राउंड

HTML, वेब को सशक्त बनाता है, क्योंकि इसके साथ काम करना आसान है. कुछ टैग की जानकारी देकर, कुछ ही सेकंड में प्रज़ेंटेशन और स्ट्रक्चर वाला पेज बनाया जा सकता है. हालांकि, एचटीएमएल अपने-आप में इतना फ़ायदेमंद नहीं होता. इंसानों के लिए टेक्स्ट पर आधारित भाषा को समझना आसान है, लेकिन मशीन को कुछ और चीज़ों की ज़रूरत होती है. डॉक्यूमेंट ऑब्जेक्ट मॉडल या DOM डालें.

जब ब्राउज़र कोई वेब पेज लोड करता है, तो वह कई दिलचस्प काम करता है. इसकी एक कोशिश है, लेखक के एचटीएमएल को लाइव दस्तावेज़ में बदलना. आम तौर पर, पेज के स्ट्रक्चर को समझने के लिए ब्राउज़र, एचटीएमएल (टेक्स्ट की स्टैटिक स्ट्रिंग) को डेटा मॉडल (ऑब्जेक्ट/नोड) में पार्स करता है. ब्राउज़र, इन नोड का एक ट्री बनाकर एचटीएमएल की हैरारकी को बनाए रखता है: DOM. DOM के बारे में सबसे अच्छी बात यह है कि यह आपके पेज का लाइव प्रतिनिधित्व करता है. हमारे द्वारा लिखे गए स्टैटिक एचटीएमएल से उलट, ब्राउज़र के बनाए हुए नोड में प्रॉपर्टी, तरीके, और सबसे अच्छी बात होती है.... इसमें प्रोग्राम की मदद से बदलाव किया जा सकता है! इसलिए हम सीधे JavaScript का इस्तेमाल करके DOM एलिमेंट बना सकते हैं:

const header = document.createElement('header');
const h1 = document.createElement('h1');
h1.textContent = 'Hello DOM';
header.appendChild(h1);
document.body.appendChild(header);

नीचे दिया गया एचटीएमएल मार्कअप बनाता है:

<body>
    <header>
    <h1>Hello DOM</h1>
    </header>
</body>

इससे अच्छा और बढ़िया है. फिर शैडो DOM क्या है?

DOM... शैडो में है

शैडो DOM दो अंतर वाला एक सामान्य DOM है: 1) इसे कैसे बनाया/इस्तेमाल किया जाता है और 2) बाकी पेज के मुकाबले यह कैसे काम करता है. आम तौर पर, DOM नोड बनाए जाते हैं और उन्हें किसी दूसरे एलिमेंट के चाइल्ड के तौर पर जोड़ा जाता है. शैडो डीओएम की मदद से, स्कोप वाला एक DOM ट्री बनाया जाता है. यह ट्री, एलिमेंट से जुड़ा तो होता है, लेकिन असल बच्चों से अलग. इस स्कोप सबट्री को शैडो ट्री कहा जाता है. जिस एलिमेंट से यह जुड़ा है वह उसका शैडो होस्ट है. शैडो में जोड़ी जाने वाली सभी चीज़ें, होस्ट करने वाले एलिमेंट में लोकल हो जाती हैं. इसमें <style> भी शामिल है. इसी तरह शैडो DOM को सीएसएस स्टाइल की स्कोपिंग मिलती है.

शैडो 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 एपीआई का भी इस्तेमाल कर सकते हैं. यह वेब है. हम आपकी मदद के लिए तैयार हैं.

यह निर्देश उन एलिमेंट की सूची के बारे में बताता है जो शैडो ट्री को होस्ट नहीं कर सकते. किसी एलिमेंट के सूची में शामिल होने की कई वजहें हो सकती हैं:

  • ब्राउज़र पहले से ही एलिमेंट (<textarea>, <input>) के लिए, खुद का इंटरनल शैडो डीओएम होस्ट करता है.
  • एलिमेंट के लिए शैडो DOM (<img>) होस्ट करने का कोई मतलब नहीं है.

उदाहरण के लिए, यह काम नहीं करता है:

    document.createElement('input').attachShadow({mode: 'open'});
    // Error. `<input>` cannot host shadow dom.

कस्टम एलिमेंट के लिए शैडो डीओएम बनाना

कस्टम एलिमेंट बनाते समय, शैडो डीओएम खास तौर पर काम करता है. किसी एलिमेंट के एचटीएमएल, सीएसएस, और JS को अलग-अलग कैटगरी में बांटने के लिए शैडो DOM का इस्तेमाल करें. इससे "वेब कॉम्पोनेंट" बनता है.

उदाहरण - कस्टम एलिमेंट अपने DOM/CSS को एन्क्रिप्ट करते हुए, शैडो DOM को खुद से जोड़ता है:

// 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>
    `;
    }
    ...
});

यहां कई दिलचस्प चीज़ें हैं. पहली वजह यह है कि <fancy-tabs> का इंस्टेंस बनाए जाने पर, कस्टम एलिमेंट अपना शैडो डीओएम बनाता है. यह constructor() में हो गया है. दूसरी वजह, हम एक शैडो रूट बना रहे हैं. इसलिए, <style> में मौजूद सीएसएस नियमों का दायरा <fancy-tabs> होगा.

कंपोज़िशन और स्लॉट

कंपोज़िशन, शैडो DOM की उन सुविधाओं में से एक है जिन्हें सबसे कम समझा जाता है, लेकिन यह सबसे अहम है.

वेब डेवलपमेंट की हमारी दुनिया में, कंपोज़िशन का मतलब है कि हम ऐप्लिकेशन कैसे बनाते हैं. यह तय करने के लिए, एचटीएमएल का इस्तेमाल किया जाता है. अलग-अलग बिल्डिंग ब्लॉक (<div>, <header>, <form>, <input>) एक साथ मिलकर ऐप्लिकेशन बनाते हैं. इनमें से कुछ टैग एक-दूसरे के साथ काम करते हैं. कंपोज़िशन की वजह से <select>, <details>, <form>, और <video> जैसे नेटिव एलिमेंट इतने सुविधाजनक होते हैं. इनमें से हर टैग कुछ खास एचटीएमएल को बच्चों के रूप में स्वीकार करता है और उनके साथ कुछ खास काम करता है. उदाहरण के लिए, <select> यह जानता है कि <option> और <optgroup> को ड्रॉपडाउन और एक से ज़्यादा विजेट में कैसे रेंडर करना है. <details> एलिमेंट, <summary> को बड़े किए जा सकने वाले ऐरो के तौर पर रेंडर करता है. <video> को भी कुछ बच्चों को मैनेज करने का तरीका पता है: <source> एलिमेंट रेंडर नहीं होते. हालांकि, इनसे वीडियो की परफ़ॉर्मेंस पर असर पड़ता है. क्या जादू है!

शब्दावली: लाइट DOM बनाम शैडो DOM

शैडो DOM कंपोज़िशन से वेब डेवलपमेंट की कुछ नई बुनियादी चीज़ों के बारे में पता चलता है. आगे बढ़ने से पहले, आइए कुछ शब्दावली पर बात करते हैं, ताकि हम एक ही भाषा में बात कर सकें.

हल्के डीओएम

वह मार्कअप जिसे आपके कॉम्पोनेंट का कोई उपयोगकर्ता लिखता है. यह डीओएम, कॉम्पोनेंट के शैडो डीओएम के बाहर होता है. यह एलिमेंट के असल चाइल्ड होते हैं.

<better-button>
    <!-- the image and span are better-button's light DOM -->
    <img src="gear.svg" slot="icon">
    <span>Settings</span>
</better-button>

शैडो डीओएम

वह डीओएम जिसे कॉम्पोनेंट का लेखक लिखता है. शैडो DOM कॉम्पोनेंट के लिए लोकल होता है. यह इसकी इंटरनल स्ट्रक्चर और स्कोप वाले सीएसएस के बारे में बताता है. साथ ही, लागू करने की जानकारी को इकट्ठा करता है. इससे यह भी तय किया जा सकता है कि आपके कॉम्पोनेंट के उपभोक्ता की ओर से बनाए गए मार्कअप को कैसे रेंडर करना है.

#shadow-root
    <style>...</style>
    <slot name="icon"></slot>
    <span id="wrapper">
    <slot>Button</slot>
    </span>

फ़्लैट किया गया डीओएम ट्री

ब्राउज़र से उपयोगकर्ता के लाइट DOM को आपके शैडो DOM में डिस्ट्रिब्यूट करने से, फ़ाइनल प्रॉडक्ट रेंडर होता है. चपटा हुआ पेड़, दरअसल आपको DevTools में दिखता है और पेज पर रेंडर किया जाता है.

<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> एलिमेंट

शैडो DOM <slot> एलिमेंट का इस्तेमाल करके, अलग-अलग डीओएम ट्री को एक साथ बनाता है. स्लॉट आपके कॉम्पोनेंट के अंदर प्लेसहोल्डर होते हैं, जिन्हें उपयोगकर्ता अपने मार्कअप से भर सकते हैं. एक या उससे ज़्यादा स्लॉट तय करने का मतलब है कि आपने अपने कॉम्पोनेंट के शैडो DOM में रेंडर करने के लिए, बाहरी मार्कअप को न्योता भेजा है. असल में, आपका यह कहना है कि "उपयोगकर्ता का मार्कअप यहां रेंडर करें".

जब <slot> किसी एलिमेंट को न्योता भेजता है, तो वह शैडो डीओएम की सीमा को "क्रॉस" कर सकता है. इन एलिमेंट को डिस्ट्रिब्यूट किए गए नोड कहा जाता है. सैद्धान्तिक तौर पर, डिस्ट्रिब्यूट किए गए नोड अजीब लग सकते हैं. स्लॉट DOM को भौतिक रूप से स्थानांतरित नहीं करते; वे उसे शैडो DOM में किसी अन्य स्थान पर रेंडर करते हैं.

कोई कॉम्पोनेंट अपने शैडो DOM में शून्य या उससे ज़्यादा स्लॉट तय कर सकता है. स्लॉट खाली हो सकते हैं या उनमें फ़ॉलबैक कॉन्टेंट दिया जा सकता है. अगर उपयोगकर्ता लाइट 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>

नाम वाले स्लॉट भी बनाए जा सकते हैं. नाम वाले स्लॉट आपके शैडो DOM में खास छेद होते हैं जिनका इस्तेमाल उपयोगकर्ता नाम से करते हैं.

उदाहरण - <fancy-tabs> के शैडो DOM में स्लॉट:

#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>

ध्यान दें कि हमारा कॉम्पोनेंट अलग-अलग कॉन्फ़िगरेशन को हैंडल कर सकता है, लेकिन फ़्लैट किया हुआ डीओएम ट्री पहले जैसा ही रहता है. हम <button> से <h2> पर स्विच भी कर सकते हैं. इस कॉम्पोनेंट को अलग-अलग तरह के बच्चों को मैनेज करने के लिए बनाया गया था... बिलकुल वैसे ही, जैसे <select> करता है!

शैलीकृत करना

वेब कॉम्पोनेंट को स्टाइल देने के कई विकल्प हैं. शैडो डीओएम का इस्तेमाल करने वाले कॉम्पोनेंट को मुख्य पेज के हिसाब से स्टाइल किया जा सकता है और उसका स्टाइल तय किया जा सकता है. इसके अलावा, डिफ़ॉल्ट को बदलने के लिए, उपयोगकर्ताओं को हुक (सीएसएस कस्टम प्रॉपर्टी के रूप में) दिए जा सकते हैं.

कॉम्पोनेंट के आधार पर तय की गई स्टाइल

शैडो डीओएम की सबसे काम की सुविधा स्कोप किया गया सीएसएस है:

  • बाहरी पेज पर मौजूद सीएसएस सिलेक्टर, आपके कॉम्पोनेंट में लागू नहीं होते.
  • अंदर परिभाषित की गई शैलियां ब्लीड नहीं करती. इनका दायरा होस्ट एलिमेंट के दायरे में होता है.

शैडो डीओएम में इस्तेमाल किए जाने वाले सीएसएस सिलेक्टर, आपके कॉम्पोनेंट पर स्थानीय तौर पर लागू होते हैं. इसका मतलब है कि हम पेज पर कहीं और मौजूद विरोधाभासों की चिंता किए बिना, सामान्य आईडी/क्लास के नामों का फिर से इस्तेमाल कर सकते हैं. सामान्य सीएसएस सिलेक्टर, 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>

आपने कभी सोचा है कि जब आप multiple एट्रिब्यूट को जोड़ते हैं, तो <select> एलिमेंट एक से ज़्यादा विकल्प वाले विजेट (ड्रॉपडाउन के बजाय) को कैसे रेंडर करता है:

<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>

:host के साथ एक समझ यह है कि पैरंट पेज के नियमों की विशेषता, एलिमेंट में तय किए गए :host नियमों से ज़्यादा होती है. यानी, बाहरी स्टाइल की जीत होती है. इससे उपयोगकर्ता, बाहर से आपकी टॉप-लेवल स्टाइल को बदल सकते हैं. साथ ही, :host सिर्फ़ शैडो रूट के संदर्भ में काम करता है. इसलिए, इसका इस्तेमाल शैडो 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) जब यह .darktheme का डिसेंडेंट होगा, तो <fancy-tabs> को स्टाइल किया जाएगा:

:host-context(.darktheme) {
    color: white;
    background: black;
}

:host-context() का इस्तेमाल थीम बनाने के लिए किया जा सकता है. हालांकि, इससे भी बेहतर तरीका यह है कि आप सीएसएस कस्टम प्रॉपर्टी का इस्तेमाल करके स्टाइल हुक बनाएं.

डिस्ट्रिब्यूट किए गए नोड को स्टाइल करना

::slotted(<compound-selector>) उन नोड से मैच करता है जो <slot> में डिस्ट्रिब्यूट होते हैं.

मान लें कि हमने नाम वाले बैज का कॉम्पोनेंट बनाया है:

<name-badge>
    <h2>Eric Bidelman</h2>
    <span class="title">
    Digital Jedi, <span class="company">Google</span>
    </span>
</name-badge>

कॉम्पोनेंट का शैडो 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> उपयोगकर्ता के लाइट DOM को ट्रांसफ़र नहीं करते. जब नोड को <slot> में डिस्ट्रिब्यूट किया जाता है, तो <slot> अपने 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>
`;

इस उदाहरण में, दो स्लॉट हैं: टैब के टाइटल के लिए एक नाम वाला स्लॉट और टैब पैनल के कॉन्टेंट के लिए एक स्लॉट. जब उपयोगकर्ता कोई टैब चुनता है, तो हम चुने गए टैब को बोल्ड करते हैं और उसका पैनल दिखाते हैं. इसके लिए, ऐसे डिस्ट्रिब्यूट किए गए नोड चुनें जिनमें selected एट्रिब्यूट है. कस्टम एलिमेंट का JS (यहां नहीं दिखाया गया है), उस एट्रिब्यूट को सही समय पर जोड़ता है.

कॉम्पोनेंट को बाहर से स्टाइल करना

किसी कॉम्पोनेंट को बाहर से स्टाइल करने के कई तरीके हैं. सबसे आसान तरीका यह है कि टैग के नाम को सिलेक्टर के तौर पर इस्तेमाल किया जाए:

fancy-tabs {
    width: 500px;
    color: red; /* Note: inheritable CSS properties pierce the shadow DOM boundary. */
}
fancy-tabs:hover {
    box-shadow: 0 3px 3px #ccc;
}

शैडो DOM में तय किए गए स्टाइल पर, बाहरी स्टाइल हमेशा बेहतर परफ़ॉर्म करते हैं. उदाहरण के लिए, अगर उपयोगकर्ता, सिलेक्टर fancy-tabs { width: 500px; } लिखता है, तो यह कॉम्पोनेंट के नियम: :host { width: 650px;} को पीछे छोड़ देगा.

कॉम्पोनेंट की स्टाइल में बदलाव करने से, आपको सिर्फ़ बेहतर नतीजे मिलेंगे. हालांकि, अगर आपको किसी कॉम्पोनेंट के अंदरूनी हिस्सों को स्टाइल करना हो, तो क्या होगा? इसके लिए, हमें सीएसएस कस्टम प्रॉपर्टी की ज़रूरत होगी.

सीएसएस कस्टम प्रॉपर्टी का इस्तेमाल करके स्टाइल हुक बनाना

अगर कॉम्पोनेंट का लेखक सीएसएस कस्टम प्रॉपर्टी का इस्तेमाल करके स्टाइल हुक उपलब्ध कराता है, तो उपयोगकर्ता अंदरूनी स्टाइल में बदलाव कर सकते हैं. सैद्धान्तिक तौर पर, आइडिया <slot> से मिलता-जुलता है. उपयोगकर्ताओं को बदलाव करने के लिए, "स्टाइल प्लेसहोल्डर" बनाएं.

उदाहरण - <fancy-tabs> की मदद से उपयोगकर्ता, बैकग्राउंड के रंग को बदल सकते हैं:

<!-- main page -->
<style>
    fancy-tabs {
    margin-bottom: 32px;
    --fancy-tabs-bg: black;
    }
</style>
<fancy-tabs background>...</fancy-tabs>

इसके शैडो DOM में:

:host([background]) {
    background: var(--fancy-tabs-bg, #9E9E9E);
    border-radius: 10px;
    padding: 10px;
}

इस मामले में, कॉम्पोनेंट, बैकग्राउंड की वैल्यू के तौर पर black का इस्तेमाल करेगा, क्योंकि उपयोगकर्ता ने इसे उपलब्ध कराया है. ऐसा न करने पर, यह डिफ़ॉल्ट रूप से #9E9E9E पर सेट हो जाएगा.

उन्नत विषय

क्लोज़्ड शैडो रूट बनाने से (बचना चाहिए)

शैडो डीओएम का एक और फ़्लेवर "क्लोज़्ड" मोड है. क्लोज़्ड शैडो ट्री बनाने पर, JavaScript से बाहर का कोई व्यक्ति आपके कॉम्पोनेंट के इंटरनल डीओएम को ऐक्सेस नहीं कर पाएगा. यह <video> जैसे नेटिव एलिमेंट के काम करने के तरीके से मिलता-जुलता है. JavaScript, <video> के शैडो DOM को ऐक्सेस नहीं कर सकता, क्योंकि ब्राउज़र इसे क्लोज़्ड मोड वाले शैडो रूट का इस्तेमाल करके लागू करता है.

उदाहरण - बंद शैडो ट्री बनाना:

const div = document.createElement('div');
const shadowRoot = div.attachShadow({mode: 'closed'}); // close shadow tree
// div.shadowRoot === null
// shadowRoot.host === div

अन्य एपीआई पर भी क्लोज़्ड मोड का असर पड़ता है:

  • Element.assignedSlot / TextNode.assignedSlot से null का डेटा मिलता है
  • शैडो के अंदर मौजूद एलिमेंट से जुड़े इवेंट के लिए Event.composedPath(), DOM दिखाता है []

आपको {mode: 'closed'} की मदद से वेब कॉम्पोनेंट क्यों नहीं बनाने चाहिए, इस बारे में खास जानकारी यहां दी गई है:

  1. सुरक्षा की आर्टिफ़िशियल भावना. किसी हमलावर को Element.prototype.attachShadow को हाइजैक करने से नहीं रोका जा सकता.

  2. क्लोज़्ड मोड की मदद से, आपके कस्टम एलिमेंट कोड को उसके शैडो डीओएम को ऐक्सेस करने से रोका जाता है. यह पूरी तरह से विफल है. इसके बजाय, अगर 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 में स्लॉट के साथ काम करना

शैडो DOM एपीआई, स्लॉट और डिस्ट्रिब्यूटेड नोड के साथ काम करने के लिए सुविधाएं उपलब्ध कराता है. ये कस्टम एलिमेंट बनाते समय काम के होते हैं.

स्लॉट बदलने का इवेंट

किसी स्लॉट के डिस्ट्रिब्यूट किए गए नोड में बदलाव होने पर slotchange इवेंट फ़ायर होता है. उदाहरण के लिए, अगर उपयोगकर्ता लाइट DOM में बच्चों को जोड़ता/हटाता है.

const slot = this.shadowRoot.querySelector('#slot');
slot.addEventListener('slotchange', e => {
    console.log('light dom children changed!');
});

लाइट डीओएम में होने वाले दूसरी तरह के बदलावों पर नज़र रखने के लिए, एलिमेंट के कंस्ट्रक्टर में MutationObserver को सेटअप किया जा सकता है.

स्लॉट में कौनसे एलिमेंट रेंडर किए जा रहे हैं?

कभी-कभी यह जानना मददगार होता है कि स्लॉट के साथ कौनसे एलिमेंट जुड़े हैं. slot.assignedNodes() को कॉल करके पता लगाएं कि स्लॉट किन एलिमेंट को रेंडर कर रहा है. अगर कोई नोड डिस्ट्रिब्यूट नहीं हो रहा है, तो {flatten: true} विकल्प किसी स्लॉट का फ़ॉलबैक कॉन्टेंट भी दिखाएगा.

उदाहरण के लिए, आपका शैडो DOM ऐसा दिखता है:

<slot><b>fallback content</b></slot>
इस्तेमाल का तरीकाकॉलनतीजा
<my-कॉम्पोनेंट>कॉम्पोनेंट टेक्स्ट</my-कॉम्पोनेंट> 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 इवेंट मॉडल

जब कोई इवेंट शैडो DOM से बबल्स के रूप में सामने आता है, तब उसके टारगेट में बदलाव किया जाता है. ऐसा, शैडो DOM से मिलने वाले एनकैप्सुलेशन को बनाए रखने के लिए किया जाता है. इसका मतलब है कि इवेंट को इस तरह से फिर से टारगेट किया जाता है कि वे आपके शैडो 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() को कॉल करने से, उन नोड का कलेक्शन दिखेगा जिनसे इवेंट गुज़रा है.

कस्टम इवेंट का इस्तेमाल करना

शैडो ट्री में इंटरनल नोड पर ट्रिगर होने वाले कस्टम डीओएम इवेंट, तब तक शैडो सीमा से बाहर नहीं जाते, जब तक इवेंट को 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>

हैंडलिंग का फ़ोकस

अगर आपको शैडो DOM के इवेंट मॉडल से याद आता है, तो शैडो 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.

अगर चलाए जाने के दौरान शैडो DOM के कई लेवल मौजूद हैं (जैसे कि किसी दूसरे कस्टम एलिमेंट में कस्टम एलिमेंट), तो activeElement ढूंढने के लिए आपको शैडो रूट में बार-बार ड्रिल-डाउन करना होगा:

function deepActiveElement() {
    let a = document.activeElement;
    while (a && a.shadowRoot && a.shadowRoot.activeElement) {
    a = a.shadowRoot.activeElement;
    }
    return a;
}

फ़ोकस करने के लिए दूसरा विकल्प delegatesFocus: true विकल्प है, जो शैडो ट्री में एलिमेंट के फ़ोकस के व्यवहार को बड़ा करता है:

  • अगर शैडो डीओएम के अंदर किसी नोड पर क्लिक किया जाता है और नोड फ़ोकस करने लायक एरिया नहीं है, तो फ़ोकस करने लायक पहले एरिया पर फ़ोकस हो जाता है.
  • जब शैडो 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() वगैरह), "क्लिक किए जा सकने वाले शैडो DOM टेक्स्ट" पर क्लिक किया गया है या इंटरनल <input> फ़ोकस है (इसमें autofocus भी शामिल है).

अगर आपको delegatesFocus: false को सेट करना होता है, तो आपको यह विकल्प दिखेगा:

डेलिगेट फ़ोकस: &#39;गलत&#39; है और इंटरनल इनपुट पर फ़ोकस है.
delegatesFocus: false और इंटरनल <input> पर फ़ोकस किया गया है.
डेलिगेट फ़ोकस: &#39;गलत&#39; और &#39;x-फ़ोकस&#39; से फ़ोकस मिलता है (जैसे कि इसमें tabindex=&#39;0&#39; है).
delegatesFocus: false और <x-focus> फ़ोकस हासिल करते हैं (उदाहरण के लिए, इसमें tabindex="0" है).
डेलिगेट फ़ोकस: &#39;गलत&#39; और &#39;क्लिक करने लायक शैडो डीओएम टेक्स्ट&#39; पर क्लिक
    किया जाता है (या एलिमेंट के शैडो डीओएम के अंदर के किसी खाली इलाके पर क्लिक किया जाता है).
delegatesFocus: false और "क्लिक करने लायक शैडो DOM टेक्स्ट" पर क्लिक किया जाता है (या एलिमेंट के शैडो DOM में मौजूद किसी खाली एरिया पर क्लिक किया जाता है).

सुझाव और तरकीबें

पिछले कुछ सालों में मैंने वेब कॉम्पोनेंट बनाने के बारे में कुछ सीखा है. हमें लगता है कि आपको इनमें से कुछ सलाह, कॉम्पोनेंट बनाने और शैडो डीओएम को डीबग करने में मददगार होंगी.

सीएसएस कंटेनमेंट का इस्तेमाल करें

आम तौर पर, वेब कॉम्पोनेंट का लेआउट/स्टाइल/पेंट अपने-आप में पूरा होता है. फ़ायदे पाने के लिए, :host में सीएसएस कंटेनमेंट का इस्तेमाल करें:

<style>
:host {
    display: block;
    contain: content; /* Boom. CSS containment FTW. */
}
</style>

इनहेरिट करने लायक स्टाइल रीसेट की जा रही हैं

इनहेरिट किए जा सकने वाले स्टाइल (background, color, font, line-height वगैरह) शैडो 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>

किसी पेज पर इस्तेमाल किए गए सभी कस्टम एलिमेंट ढूंढना

कभी-कभी पेज पर इस्तेमाल किए गए कस्टम एलिमेंट ढूंढना मददगार होता है. ऐसा करने के लिए, आपको पेज पर इस्तेमाल किए गए सभी एलिमेंट के शैडो डीओएम को बार-बार ट्रेस करना होगा.

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> का इस्तेमाल करते हैं. टेंप्लेट, किसी वेब कॉम्पोनेंट के स्ट्रक्चर का एलान करने के लिए सबसे सही प्लेसहोल्डर होते हैं.

"कस्टम एलिमेंट: फिर से इस्तेमाल किए जा सकने वाले वेब कॉम्पोनेंट बनाना" में दिया गया उदाहरण देखें.

इतिहास और ब्राउज़र सहायता

अगर पिछले कुछ सालों से वेब कॉम्पोनेंट को फ़ॉलो किया जा रहा है, तो आपको पता होगा कि Chrome 35+/Opera पर कुछ समय से शैडो DOM के पुराने वर्शन की शिपिंग की जा रही है. Blink कुछ समय तक साथ-साथ दोनों वर्शन पर काम करता रहेगा. शैडो रूट बनाने के लिए v0 स्पेसिफ़िकेशन में अलग तरीका दिया गया है (v1 के element.attachShadow के बजाय element.createShadowRoot). पुराने तरीके का इस्तेमाल करने से v0 सिमैंटिक वाला शैडो रूट बनता है, ताकि मौजूदा v0 कोड काम न करे.

अगर आपकी दिलचस्पी पुराने v0 वर्शन में है, तो html5rocks के लेख देखें: 1, 2, 3. शैडो DOM v0 और v1 के बीच के अंतर की काफ़ी तुलना भी की गई है.

ब्राउज़र समर्थन

Chrome 53 (स्थिति), ऑपरा 40, Safari 10, और Firefox 63 में Shadow DOM v1 भेजा जाता है. एज ने डेवलपमेंट शुरू कर दिया है.

शैडो डीओएम का पता लगाने की सुविधा के लिए, देखें कि attachShadow मौजूद है या नहीं:

const supportsShadowDOMV1 = !!HTMLElement.prototype.attachShadow;

पॉलीफ़िल

जब तक ब्राउज़र पर पूरी तरह से काम करने की सुविधा उपलब्ध नहीं हो जाती, तब तक छाया और shadycs पॉलीफ़िल आपको v1 सुविधा देते हैं. शेडी DOM, Shadow DOM की DOM स्कोपिंग और shadycs पॉलीफ़िल CSS कस्टम प्रॉपर्टी की नकल करता है और नेटिव एपीआई से मिलने वाली स्टाइल की स्कोपिंग करता है.

पॉलीफ़िल इंस्टॉल करें:

bower install --save webcomponents/shadydom
bower install --save webcomponents/shadycss

पॉलीफ़िल का इस्तेमाल करें:

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 देखें.

नतीजा

हमारे पास पहली बार ऐसा एपीआई प्रिमिटिव है जो सही सीएसएस स्कोपिंग, डीओएम स्कोपिंग करता है, और इसमें सही कंपोज़िशन है. दूसरे वेब कॉम्पोनेंट एपीआई जैसे कस्टम एलिमेंट के साथ शैडो डीओएम, कॉम्पोनेंट को बिना हैक किए या <iframe>s जैसे पुराने बैगेज का इस्तेमाल करने के लिए सही तरीके से एनकैप्सुलेट किया जाता है.

मेरी बात ग़लत मत समझो. शैडो डीओएम निश्चित तौर पर एक मुश्किल बीस्ट है! लेकिन यह सीखना काफ़ी बेहद अहम है. इसके साथ कुछ समय बिताएं. इसे सीखें और सवाल पूछें!

इसके बारे में और पढ़ें

अक्सर पूछे जाने वाले सवाल

क्या आज Shadow DOM v1 का इस्तेमाल किया जा सकता है?

पॉलीफ़िल के साथ, हां. ब्राउज़र सहायता देखें.

शैडो डीओएम से सुरक्षा से जुड़ी कौनसी सुविधाएं मिलती हैं?

शैडो DOM सुरक्षा से जुड़ी सुविधा नहीं है. यह सीएसएस को स्कोप करने और कॉम्पोनेंट में डीओएम ट्री को छिपाने का एक आसान टूल है. अगर आपको सुरक्षा सीमा के बारे में सही जानकारी चाहिए, तो <iframe> का इस्तेमाल करें.

क्या वेब कॉम्पोनेंट में शैडो डीओएम का इस्तेमाल करना ज़रूरी है?

नहीं! आपको शैडो DOM का इस्तेमाल करने वाले वेब कॉम्पोनेंट बनाने की ज़रूरत नहीं होती. हालांकि, शैडो डीओएम का इस्तेमाल करने वाले कस्टम एलिमेंट लिखने का मतलब है कि सीएसएस स्कोपिंग, डीओएम एनकैप्सुलेशन, और कंपोज़िशन जैसी सुविधाओं का फ़ायदा लिया जा सकता है.

ओपन और क्लोज़्ड शैडो रूट में क्या अंतर है?

बंद शैडो रूट देखें.