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

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

खास जानकारी

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

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

परिचय

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

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

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

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

fancy-tabs डेमो

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

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

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

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

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

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

उदाहरण - पसंद के मुताबिक बनाया गया एलिमेंट, अपने डीओएम/सीएसएस को अपने-आप शैडो डीओएम से जोड़ता है:

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

शैडो DOM

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

#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 कॉन्टेंट नहीं देता है, तो स्लॉट अपना फ़ॉलबैक कॉन्टेंट रेंडर करता है.

<!-- 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 सिर्फ़ शैडो रूट के संदर्भ में काम करता है. इसलिए, इसका इस्तेमाल शैडो 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(), थीम बनाने के लिए काम का हो सकता है. हालांकि, सीएसएस कस्टम प्रॉपर्टी का इस्तेमाल करके स्टाइल हुक बनाना, इससे भी बेहतर तरीका है.

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

::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> के शैडो 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 में स्लॉट के साथ काम करना

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

slotchange इवेंट

जब किसी स्लॉट के डिस्ट्रिब्यूट किए गए नोड में बदलाव होता है, तो 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 से बाहर भी नहीं भेजे जाते.

शैडो बाउंड्री को पार करने वाले इवेंट:

  • फ़ोकस इवेंट: 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>

नतीजा

delegatesFocus: सही व्यवहार.

ऊपर दिया गया नतीजा तब दिखता है, जब <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" है).
delegatesFocus: false है और &#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. इसमें शैडो डीओएम v0 और v1 के बीच के अंतर की बेहतरीन तुलना भी की गई है.

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

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

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

const supportsShadowDOMV1 = !!HTMLElement.prototype.attachShadow;

Polyfill

जब तक ब्राउज़र के लिए यह सुविधा सभी के लिए उपलब्ध नहीं हो जाती, तब तक shadydom और shadycss पॉलीफ़िल की मदद से, आपको वर्शन 1 की सुविधा मिलती है. 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> का इस्तेमाल करें.

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

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

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

क्लोज़्ड शैडो रूट देखें.