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

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

खास जानकारी

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

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

परिचय

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

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

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

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

fancy-tabs डेमो

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

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

शैडो डीओएम क्या है?

डीओएम के बारे में जानकारी

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

जब ब्राउज़र कोई वेब पेज लोड करता है, तो वह कई दिलचस्प काम करता है. यह सबसे ज़्यादा होने वाला एक काम है, लेखक के एचटीएमएल को लाइव दस्तावेज़ में बदलना. आम तौर पर, पेज के स्ट्रक्चर को समझने के लिए, ब्राउज़र एचटीएमएल (टेक्स्ट की स्टैटिक स्ट्रिंग) को डेटा मॉडल (ऑब्जेक्ट/नोड) में पार्स करता है. ब्राउज़र इन नोड का ट्री बनाकर, एचटीएमएल की हैरारकी को बनाए रखता है: डीओएम. 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 की तरह ही होता है. हालांकि, इन दोनों में दो अंतर होते हैं: 1) इसे बनाने/इस्तेमाल करने का तरीका और 2) यह पेज के बाकी हिस्से के साथ कैसे काम करता है. आम तौर पर, डीओएम नोड बनाए जाते हैं और उन्हें किसी दूसरे एलिमेंट के चाइल्ड के तौर पर जोड़ा जाता है. शैडो डीओएम की मदद से, स्कोप वाला डीओएम ट्री बनाया जाता है. यह ट्री, एलिमेंट से जुड़ा होता है, लेकिन उसके असली चाइल्ड से अलग होता है. स्कोप वाले इस सबट्री को शैडो ट्री कहा जाता है. यह जिस एलिमेंट से जुड़ा होता है वह शैडो होस्ट है. शेडो में जो भी जोड़ा जाता है वह होस्ट करने वाले एलिमेंट के लिए लोकल हो जाता है. इसमें <style> भी शामिल है. इस तरह शैडो डीओएम, सीएसएस स्टाइल की स्कोपिंग को हासिल करता है.

शैडो डीओएम बनाना

शैडो रूट दस्तावेज़ का एक हिस्सा होता है, जो किसी “होस्ट” एलिमेंट के साथ जुड़ जाता है. शैडो रूट को अटैच करने की प्रोसेस से, एलिमेंट को शैडो डीओएम मिलता है. किसी एलिमेंट के लिए शैडो डीओएम बनाने के लिए, 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 का इस्तेमाल करने का विकल्प भी है. यह वेब है. हमारे पास विकल्प है.

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

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

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

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

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

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

उदाहरण - कोई कस्टम एलिमेंट शैडो डीओएम को खुद से जोड़ता है और इसके 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>
    `;
    }
    ...
});

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

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

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

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

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

शैडो डीओएम कॉम्पोज़िशन, वेब डेवलपमेंट में कई नए बुनियादी सिद्धांतों को पेश करता है. ज़्यादा जानकारी देने से पहले, कुछ शब्दों को स्टैंडर्ड बना दें, ताकि हम एक ही भाषा में बात कर सकें.

लाइट डीओएम

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

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

शैडो डीओएम

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

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

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

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

शैडो डीओएम, <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> के शैडो डीओएम में स्लॉट:

#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 की सबसे ज़्यादा काम की सुविधा, स्कोप वाली सीएसएस है:

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

शैडो डीओएम में इस्तेमाल किए जाने वाले सीएसएस सिलेक्टर, आपके कॉम्पोनेंट पर स्थानीय तौर पर लागू होते हैं. इसका मतलब है कि हम पेज पर कहीं और होने वाले संघर्षों के बारे में चिंता किए बिना, फिर से सामान्य आईडी/क्लास के नामों का इस्तेमाल कर सकते हैं. शैडो डीओएम में, आसान सीएसएस सिलेक्टर इस्तेमाल करना सबसे सही तरीका है. ये परफ़ॉर्मेंस के लिए भी अच्छे होते हैं.

उदाहरण - शैडो रूट में तय की गई स्टाइल, लोकल होती हैं

#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 सिर्फ़ शैडो रूट के कॉन्टेक्स्ट में काम करता है. इसलिए, इसका इस्तेमाल शैडो डीओएम के बाहर नहीं किया जा सकता.

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

जब यह .darktheme का डिसेंडेंट होगा, तो :host-context(.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>

कॉम्पोनेंट का शैडो डीओएम, उपयोगकर्ता के <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>s, उपयोगकर्ता के लाइट डीओएम को नहीं ले जाते. जब नोड को <slot> में बांटा जाता है, तो <slot> उनके डीओएम को रेंडर करता है, लेकिन नोड अपनी जगह पर बने रहते हैं. डिस्ट्रिब्यूशन से पहले लागू किए गए स्टाइल, डिस्ट्रिब्यूशन के बाद भी लागू रहते हैं. हालांकि, जब लाइट डीओएम डिस्ट्रिब्यूट किया जाता है, तब यह अन्य स्टाइल (शैडो डीओएम की ओर से तय किए गए स्टाइल) अपना सकता है.

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

शैडो डीओएम में तय की गई स्टाइल के मुकाबले, बाहरी स्टाइल हमेशा प्राथमिकता पाती हैं. उदाहरण के लिए, अगर उपयोगकर्ता सिलेक्टर 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>

शैडो डीओएम में:

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

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

उन्नत विषय

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

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

उदाहरण - क्लोज़्ड शैडो ट्री बनाना:

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. क्लोज़्ड मोड आपके कस्टम एलिमेंट कोड को उसके शैडो 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 में स्लॉट के साथ काम करना

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

स्लॉटचेंज इवेंट

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

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-component>component text</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 से पता चलता है कि आपका एलिमेंट, कॉम्पोनेंट के किस स्लॉट में असाइन किया गया है.

शैडो डीओएम इवेंट मॉडल

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

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

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

अगर शैडो डीओएम के कई लेवल हैं, जैसे कि किसी कस्टम एलिमेंट में एक और कस्टम एलिमेंट, तो activeElement ढूंढने के लिए, आपको शैडो रूट में बार-बार ड्रिल करना होगा:

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

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

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

अगर आपको delegatesFocus: false को सेट करना होता, तो इसके बजाय आपको यह दिखेगा:

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

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

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

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

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

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

इनहेरिट की जा सकने वाली स्टाइल रीसेट करना

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

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

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

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 कुछ समय तक, दोनों वर्शन के साथ काम करता रहेगा. वर्शन 0 के स्पेसिफ़िकेशन में, शैडो रूट बनाने के लिए एक अलग तरीका दिया गया था (element.attachShadow के बजाय element.createShadowRoot). पुराने तरीके का इस्तेमाल करने पर, वर्शन 0 के सेमेंटेक्स के साथ शैडो रूट बनता रहेगा, ताकि वर्शन 0 का मौजूदा कोड काम करता रहे.

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

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

Shadow DOM v1, Chrome 53 (status), Opera 40, Safari 10, और Firefox 63 में शिप किया गया है. Edge ने डेवलपमेंट शुरू कर दिया है.

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

const supportsShadowDOMV1 = !!HTMLElement.prototype.attachShadow;

Polyfill

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

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

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> जैसे पुराने बैगेज का इस्तेमाल किए बिना, असल में कंटेनप्लेट किए गए कॉम्पोनेंट बनाने का एक तरीका उपलब्ध कराता है.

मुझे गलत मत समझें. शैडो डीओएम वाकई एक जटिल चीज़ है! हालांकि, यह एक ऐसा विषय है जिसे सीखना ज़रूरी है. कुछ समय इसके साथ बिताएं. इसे जानें और सवाल पूछें!

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

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

क्या आज शैडो डीओएम v1 का इस्तेमाल किया जा सकता है?

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

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

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

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

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

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

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