डिक्लेरेटिव शैडो डीओएम, वेब प्लैटफ़ॉर्म की स्टैंडर्ड सुविधा है. यह Chrome के 90 वर्शन से काम करती है. ध्यान दें कि 2023 में इस सुविधा के स्पेसिफ़िकेशन में बदलाव हुए थे. इनमें, shadowroot
का नाम बदलकर shadowrootmode
किया गया था. इस सुविधा के सभी हिस्सों के सबसे अप-टू-डेट स्टैंडर्ड वर्शन, Chrome वर्शन 124 में लॉन्च कर दिए गए हैं.
ब्राउज़र सहायता
-
अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
-
अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
-
अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
-
अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
शैडो DOM तीन वेब कॉम्पोनेंट के स्टैंडर्ड में से एक है. इसे एचटीएमएल टेंप्लेट और कस्टम एलिमेंट के ज़रिए राउंड आउट किया जाता है. शैडो डीओएम से किसी खास डीओएम सबट्री को सीएसएस स्टाइल के दायरे में लाने और उस सबट्री को दस्तावेज़ के बाकी हिस्सों से अलग करने का तरीका मिलता है. <slot>
एलिमेंट की मदद से, हम यह कंट्रोल कर सकते हैं कि किसी कस्टम एलिमेंट के चाइल्ड एंट्री को उसके शैडो ट्री में कहां डाला जाना चाहिए. इन सभी सुविधाओं के इस्तेमाल से सिस्टम, अपने-आप में पूरे होने वाले और फिर से इस्तेमाल किए जा सकने वाले कॉम्पोनेंट बनाने में मदद करता है. ये कॉम्पोनेंट पहले से मौजूद एचटीएमएल एलिमेंट की तरह ही, मौजूदा ऐप्लिकेशन के साथ आसानी से इंटिग्रेट हो जाते हैं.
अब तक, Shadow DOM का उपयोग करने का एकमात्र तरीका JavaScript का उपयोग करके शैडो रूट बनाना था:
const host = document.getElementById('host');
const shadowRoot = host.attachShadow({mode: 'open'});
shadowRoot.innerHTML = '<h1>Hello Shadow DOM</h1>';
इस तरह का एक ज़रूरी एपीआई, क्लाइंट-साइड रेंडरिंग के लिए सही तरीके से काम करता है: हमारे कस्टम एलिमेंट को तय करने वाले JavaScript मॉड्यूल ही अपने शैडो रूट बनाते हैं और उनका कॉन्टेंट सेट करते हैं. हालांकि, कई वेब ऐप्लिकेशन को बिल्ड के समय कॉन्टेंट को सर्वर-साइड या स्टैटिक एचटीएमएल में रेंडर करना होगा. यह उन विज़िटर को उचित अनुभव देने का एक महत्वपूर्ण हिस्सा हो सकता है, जो शायद JavaScript चलाने में असमर्थ हों.
सर्वर-साइड रेंडरिंग (एसएसआर) की वजहें हर प्रोजेक्ट के हिसाब से अलग-अलग होती हैं. कुछ वेबसाइटों को सुलभता से जुड़े दिशा-निर्देशों का पालन करने के लिए, पूरी तरह से काम करने वाला सर्वर के ज़रिए रेंडर किया गया एचटीएमएल देना होगा. वहीं कुछ वेबसाइटों को यह पक्का करना होगा कि वे धीमे कनेक्शन या डिवाइसों पर अच्छा परफ़ॉर्म करें. इसके लिए, वे बिना JavaScript का बेसलाइन अनुभव देने का विकल्प चुनती हैं.
अब तक, सर्वर-साइड रेंडरिंग के साथ शैडो DOM का इस्तेमाल करना मुश्किल रहा है. ऐसा इसलिए, क्योंकि सर्वर से जनरेट किए गए एचटीएमएल में शैडो रूट को एक्सप्रेस करने का कोई तरीका पहले से मौजूद नहीं था. शैडो रूट को डीओएम एलिमेंट में अटैच करने पर, परफ़ॉर्मेंस पर भी असर पड़ता है. ये उन डीओएम एलिमेंट होते हैं जो इनके बिना पहले ही रेंडर किए जा चुके हैं. इसकी वजह से, पेज लोड होने के बाद लेआउट शिफ़्ट हो सकता है या Shadow Root की स्टाइलशीट लोड करते समय बिना स्टाइल वाली सामग्री ("FOUC") का फ़्लैश दिखा सकता है.
डिक्लेरेटिव शैडो डीओएम (डीएसडी) इस पाबंदी को हटा देता है और शैडो डीओएम को सर्वर पर ला देता है.
डिक्लेरेटिव शैडो रूट बनाने का तरीका
डिक्लेरेटिव शैडो रूट, shadowrootmode
एट्रिब्यूट वाला <template>
एलिमेंट है:
<host-element>
<template shadowrootmode="open">
<slot></slot>
</template>
<h2>Light content</h2>
</host-element>
एचटीएमएल पार्सर, shadowrootmode
एट्रिब्यूट वाले टेंप्लेट एलिमेंट की पहचान करता है और उसे तुरंत अपने पैरंट एलिमेंट के शैडो रूट के तौर पर लागू कर देता है. ऊपर दिए गए सैंपल नतीजों से प्योर एचटीएमएल मार्कअप को नीचे दिए गए डीओएम ट्री में लोड करने से लोड होता है:
<host-element>
#shadow-root (open)
<slot>
↳
<h2>Light content</h2>
</slot>
</host-element>
यह कोड सैंपल, Chrome DevTools के एलिमेंट पैनल के उन तरीकों का पालन करता है जो शैडो डीओएम कॉन्टेंट दिखाने के लिए इस्तेमाल किए गए हैं. उदाहरण के लिए, ↳
वर्ण, स्लॉट किए गए लाइट डीओएम कॉन्टेंट के बारे में बताता है.
इससे हमें स्टैटिक एचटीएमएल में शैडो DOM के एनकैप्सुलेशन और स्लॉट प्रोजेक्शन के फ़ायदे मिलते हैं. शैडो रूट के साथ-साथ पूरा ट्री बनाने के लिए किसी भी JavaScript की ज़रूरत नहीं होती.
कॉम्पोनेंट हाइड्रेशन
डिक्लेरेटिव शैडो DOM का इस्तेमाल स्टाइल को एनकैप्सुलेट करने या चाइल्ड प्लेसमेंट को पसंद के मुताबिक बनाने के लिए किया जा सकता है. हालांकि, कस्टम एलिमेंट के साथ इस्तेमाल करने पर यह सबसे ज़्यादा असरदार होता है. कस्टम एलिमेंट का इस्तेमाल करके बनाए गए कॉम्पोनेंट, स्टैटिक एचटीएमएल से अपने-आप अपग्रेड हो जाते हैं. डिक्लेरेटिव शैडो डीओएम की शुरुआत होने के बाद, अब किसी कस्टम एलिमेंट के अपग्रेड होने से पहले उसमें शैडो रूट बनाया जा सकता है.
एचटीएमएल से अपग्रेड किए जा रहे कस्टम एलिमेंट में शैडो रूट पहले से ही अटैच होगा. इसमें डिक्लेरेटिव शैडो रूट भी शामिल होगा. इसका मतलब है कि इंस्टैंशिएट किए जाने पर, एलिमेंट में shadowRoot
प्रॉपर्टी पहले से ही उपलब्ध होगी. इसमें, आपका कोड साफ़ तौर पर नया कोड नहीं बनाएगा. अपने एलिमेंट के कंस्ट्रक्टर में, किसी मौजूदा शैडो रूट के लिए this.shadowRoot
की जांच करना सबसे अच्छा होता है. अगर पहले से कोई वैल्यू मौजूद है, तो इस कॉम्पोनेंट के एचटीएमएल में डिक्लेरेटिव शैडो रूट शामिल किया जाता है. अगर वैल्यू शून्य है, तो इसका मतलब है कि एचटीएमएल में कोई डिक्लेरेटिव शैडो रूट मौजूद नहीं था या ब्राउज़र डिक्लेरेटिव शैडो DOM पर काम नहीं करता.
<menu-toggle>
<template shadowrootmode="open">
<button>
<slot></slot>
</button>
</template>
Open Menu
</menu-toggle>
<script>
class MenuToggle extends HTMLElement {
constructor() {
super();
// Detect whether we have SSR content already:
if (this.shadowRoot) {
// A Declarative Shadow Root exists!
// wire up event listeners, references, etc.:
const button = this.shadowRoot.firstElementChild;
button.addEventListener('click', toggle);
} else {
// A Declarative Shadow Root doesn't exist.
// Create a new shadow root and populate it:
const shadow = this.attachShadow({mode: 'open'});
shadow.innerHTML = `<button><slot></slot></button>`;
shadow.firstChild.addEventListener('click', toggle);
}
}
}
customElements.define('menu-toggle', MenuToggle);
</script>
कस्टम एलिमेंट काफ़ी समय से इस्तेमाल किए जा रहे हैं और अब तक attachShadow()
का इस्तेमाल करके शैडो रूट बनाने से पहले, मौजूदा शैडो रूट की जांच करने की कोई ज़रूरत नहीं थी. डिक्लेरेटिव शैडो डीओएम में एक छोटा सा बदलाव होता है, जो इसके बावजूद मौजूदा कॉम्पोनेंट को काम करने देता है: किसी मौजूदा घोषणा वाले शैडो रूट वाले एलिमेंट पर attachShadow()
तरीके को कॉल करने से गड़बड़ी नहीं होगी. इसके बजाय, डिक्लेरेटिव शैडो रूट को खाली करके वापस भेज दिया जाता है. इससे डिक्लेरेटिव शैडो डीओएम के लिए बनाए गए पुराने कॉम्पोनेंट को काम करने की अनुमति नहीं मिलती. इसकी वजह यह है कि डिक्लेरेटिव रूट तब तक सुरक्षित रहते हैं, जब तक ज़रूरी बदलाव नहीं किया जाता.
नए कस्टम एलिमेंट के लिए, नई ElementInternals.shadowRoot
प्रॉपर्टी में किसी एलिमेंट के मौजूदा डिक्लेरेटिव शैडो रूट का रेफ़रंस दिया गया है. इसमें, ओपन और क्लोज़्ड, दोनों तरह के कस्टम एलिमेंट शामिल हैं. इसका इस्तेमाल किसी भी डिक्लेरेटिव शैडो रूट की जांच करने और उसे इस्तेमाल करने के लिए किया जा सकता है. साथ ही, अगर आपने कोई शैडो रूट नहीं दिया है, तो इस तरीके से attachShadow()
पर वापस आया जा सकता है.
class MenuToggle extends HTMLElement {
constructor() {
super();
const internals = this.attachInternals();
// check for a Declarative Shadow Root:
let shadow = internals.shadowRoot;
if (!shadow) {
// there wasn't one. create a new Shadow Root:
shadow = this.attachShadow({
mode: 'open'
});
shadow.innerHTML = `<button><slot></slot></button>`;
}
// in either case, wire up our event listener:
shadow.firstChild.addEventListener('click', toggle);
}
}
customElements.define('menu-toggle', MenuToggle);
हर रूट पर एक शैडो
डिक्लेरेटिव शैडो रूट सिर्फ़ अपने पैरंट एलिमेंट से जुड़ा होता है. इसका मतलब है कि शैडो रूट हमेशा अपने संबंधित एलिमेंट के साथ लोकेशन में होते हैं. डिज़ाइन से जुड़े इस फ़ैसले से यह पक्का हो जाता है कि शैडो रूट को एचटीएमएल दस्तावेज़ के बाकी हिस्से की तरह स्ट्रीम किया जा सकता है. यह संलेखन और जनरेट करने के लिए भी सुविधाजनक है, क्योंकि किसी तत्व में शैडो रूट जोड़ने के लिए मौजूदा शैडो रूट की रजिस्ट्री को बनाए रखने की ज़रूरत नहीं होती.
शैडो रूट को उनके पैरंट एलिमेंट से जोड़ने में हालांकि दिक्कत यह है कि कई एलिमेंट को एक ही डिक्लेरेटिव शैडो रूट <template>
से शुरू नहीं किया जा सकता. हालांकि, ज़्यादातर मामलों में इस बात की संभावना कम होती है, जहां डिक्लेरेटिव शैडो DOM का इस्तेमाल किया जाता है. इसकी वजह यह है कि हर शैडो रूट का कॉन्टेंट शायद ही एक जैसा होता है. सर्वर के रेंडर किए गए एचटीएमएल में अक्सर दोहराए जाने वाले एलिमेंट स्ट्रक्चर होते हैं, लेकिन आम तौर पर उनका कॉन्टेंट अलग होता है. उदाहरण के लिए, टेक्स्ट या एट्रिब्यूट में थोड़ा अंतर. सीरियल के डिक्लेरेटिव शैडो रूट का कॉन्टेंट पूरी तरह से स्टैटिक होता है. इसलिए, एक डिक्लेरेटिव शैडो रूट से कई एलिमेंट को अपग्रेड करने की सुविधा सिर्फ़ तब काम करेगी, जब एलिमेंट एक जैसे होंगे. आखिर में, कंप्रेशन के असर की वजह से, नेटवर्क ट्रांसफ़र साइज़ पर बार-बार एक जैसे शैडो रूट का असर कम पड़ता है.
आने वाले समय में, ऐसा हो सकता है कि शैडो रूट का फिर से इस्तेमाल किया जा सके. अगर डीओएम को पहले से मौजूद टेंप्लेट के लिए सहायता मिलती है, तो डिक्लेरेटिव शैडो रूट को ऐसे टेंप्लेट के तौर पर माना जा सकता है जिन्हें इंस्टैंशिएट किया गया हो, ताकि किसी दिए गए एलिमेंट का शैडो रूट बनाया जा सके. डिक्लेरेटिव शैडो डीओएम डिज़ाइन की मदद से, आने वाले समय में शैडो रूट असोसिएशन को किसी एक एलिमेंट तक सीमित करके, इस संभावना को बनाए रखा जा सकता है.
स्ट्रीमिंग अच्छी है
डिक्लेरेटिव शैडो रूट को सीधे उनके पैरंट एलिमेंट से जोड़ने से, अपग्रेड करने और उस एलिमेंट के साथ अटैच करने की प्रोसेस आसान हो जाती है. डिक्लेरेटिव शैडो रूट का पता एचटीएमएल पार्स करने के दौरान लगाया जाता है. साथ ही, उनका ओपनिंग <template>
टैग मिलते ही, ये रूट अटैच हो जाते हैं. <template>
में पार्स किए गए एचटीएमएल को सीधे शैडो रूट में पार्स किया जाता है, ताकि इसे "स्ट्रीम" किया जा सके. जैसे ही यह मिलता है, रेंडर किया जा सकता है.
<div id="el">
<script>
el.shadowRoot; // null
</script>
<template shadowrootmode="open">
<!-- shadow realm -->
</template>
<script>
el.shadowRoot; // ShadowRoot
</script>
</div>
सिर्फ़ पार्सर
डिक्लेरेटिव शैडो DOM, एचटीएमएल पार्सर की एक सुविधा है. इसका मतलब है कि डिक्लेरेटिव शैडो रूट को सिर्फ़ shadowrootmode
एट्रिब्यूट वाले <template>
टैग के लिए पार्स और अटैच किया जाएगा, जो एचटीएमएल पार्स करते समय मौजूद होते हैं. दूसरे शब्दों में, शुरुआती एचटीएमएल पार्स करने के दौरान डिक्लेरेटिव शैडो रूट बनाए जा सकते हैं:
<some-element>
<template shadowrootmode="open">
shadow root content for some-element
</template>
</some-element>
<template>
एलिमेंट के shadowrootmode
एट्रिब्यूट को सेट करने से कुछ नहीं होता. टेंप्लेट, सामान्य टेंप्लेट एलिमेंट ही रहता है:
const div = document.createElement('div');
const template = document.createElement('template');
template.setAttribute('shadowrootmode', 'open'); // this does nothing
div.appendChild(template);
div.shadowRoot; // null
सुरक्षा से जुड़ी कुछ ज़रूरी बातों से बचने के लिए, innerHTML
या insertAdjacentHTML()
जैसे फ़्रैगमेंट पार्स करने वाले एपीआई का इस्तेमाल करके भी, एलान वाले शैडो रूट नहीं बनाए जा सकते. डिक्लेरेटिव शैडो रूट की मदद से एचटीएमएल को पार्स करने के लिए, setHTMLUnsafe()
या parseHTMLUnsafe()
का इस्तेमाल करें:
<script>
const html = `
<div>
<template shadowrootmode="open"></template>
</div>
`;
const div = document.createElement('div');
div.innerHTML = html; // No shadow root here
div.setHTMLUnsafe(html); // Shadow roots included
const newDocument = Document.parseHTMLUnsafe(html); // Also here
</script>
स्टाइल के साथ सर्वर रेंडर करना
इनलाइन और बाहरी स्टाइलशीट, स्टैंडर्ड <style>
और <link>
टैग का इस्तेमाल करके डिक्लेरेटिव शैडो रूट में पूरी तरह से काम करती हैं:
<nineties-button>
<template shadowrootmode="open">
<style>
button {
color: seagreen;
}
</style>
<link rel="stylesheet" href="/comicsans.css" />
<button>
<slot></slot>
</button>
</template>
I'm Blue
</nineties-button>
इस तरीके से तय की गई स्टाइल को भी काफ़ी हद तक ऑप्टिमाइज़ किया जाता है: अगर एक ही स्टाइल शीट कई डिक्लेरेटिव शैडो रूट में मौजूद है, तो इसे सिर्फ़ एक बार लोड और पार्स किया जाता है. ब्राउज़र, एक बैकिंग CSSStyleSheet
का इस्तेमाल करता है, जो सभी शैडो रूट के साथ शेयर किया जाता है. इससे, डुप्लीकेट मेमोरी ओवरहेड हट जाता है.
डिक्लेरेटिव शैडो DOM में कंस्ट्रक्शन करने लायक स्टाइलशीट का इस्तेमाल नहीं किया जा सकता. इसकी वजह यह है कि फ़िलहाल, एचटीएमएल में कंस्ट्रक्शन वाली स्टाइलशीट को क्रम में लगाने का कोई तरीका नहीं है. साथ ही, adoptedStyleSheets
की जानकारी अपने-आप भरने के दौरान, इन स्टाइलशीट को रेफ़र करने का कोई तरीका भी नहीं है.
बिना स्टाइल वाले कॉन्टेंट के फ़्लैश से बचने का तरीका
जिन ब्राउज़र पर डिक्लेरेटिव शैडो डीओएम काम नहीं करता उनकी एक संभावित समस्या "बिना स्टाइल वाले कॉन्टेंट के फ़्लैश" से बचना है (FOUC), जहां ऐसे कस्टम एलिमेंट के लिए रॉ कॉन्टेंट दिखाया जाता है जिन्हें अब तक अपग्रेड नहीं किया गया है. डिक्लेरेटिव शैडो DOM से पहले, एफ़ओयूसी से बचने का एक आम तरीका यह था कि उन कस्टम एलिमेंट पर display:none
स्टाइल नियम लागू किया जाना जो अब तक लोड नहीं हुए हैं. इसकी वजह यह है कि इन एलिमेंट में शैडो रूट अटैच और पॉप्युलेट नहीं किया गया है. इस तरह, कॉन्टेंट तब तक नहीं दिखता, जब तक कि वह "तैयार न हो":
<style>
x-foo:not(:defined) > * {
display: none;
}
</style>
डिक्लेरेटिव शैडो डीओएम की मदद से, कस्टम एलिमेंट को एचटीएमएल में इस तरह रेंडर या लिखा जा सकता है कि उनका शैडो कॉन्टेंट अपनी जगह पर मौजूद हो और क्लाइंट-साइड कॉम्पोनेंट के लागू होने से पहले तैयार हो:
<x-foo>
<template shadowrootmode="open">
<style>h2 { color: blue; }</style>
<h2>shadow content</h2>
</template>
</x-foo>
इस मामले में, display:none
"FOUC" नियम, डिक्लेरेटिव शैडो रूट के कॉन्टेंट को दिखने से रोकेगा. हालांकि, इस नियम को हटाने से, डिक्लेरेटिव शैडो डीओएम सपोर्ट वाले ब्राउज़र में गलत या बिना स्टाइल वाला कॉन्टेंट तब तक दिखेगा, जब तक कि डिक्लेरेटिव शैडो डीओएम पॉलीफ़िल लोड नहीं हो जाता और शैडो रूट टेंप्लेट असली शैडो रूट में नहीं बदल देता.
अच्छी बात यह है कि इसे एफ़ओयूसी शैली के नियम में बदलाव करके सीएसएस में हल किया जा सकता है. डिक्लेरेटिव शैडो डीओएम के साथ काम करने वाले ब्राउज़र में, <template shadowrootmode>
एलिमेंट को शैडो रूट में तुरंत बदल दिया जाता है. डीओएम ट्री में कोई <template>
एलिमेंट नहीं रहता. डिक्लेरेटिव शैडो डीओएम के साथ काम न करने वाले ब्राउज़र, <template>
एलिमेंट को सुरक्षित रखते हैं. इसका इस्तेमाल, एफ़ओयूसी को रोकने के लिए किया जा सकता है:
<style>
x-foo:not(:defined) > template[shadowrootmode] ~ * {
display: none;
}
</style>
अभी तक तय नहीं किए गए कस्टम एलिमेंट को छिपाने के बजाय, बदला गया "FOUC" जब नियम <template shadowrootmode>
एलिमेंट को फ़ॉलो करते हैं, तो नियम अपने छोटे बच्चों को छिपा देता है. कस्टम एलिमेंट तय होने के बाद, नियम मेल नहीं खाता. इस नियम को उन ब्राउज़र में अनदेखा कर दिया जाता है जिनमें डिक्लेरेटिव शैडो डीओएम काम करता है, क्योंकि एचटीएमएल पार्स करने के दौरान <template shadowrootmode>
चाइल्ड को हटा दिया जाता है.
सुविधा की पहचान और ब्राउज़र समर्थन
डिक्लेरेटिव शैडो DOM, Chrome 90 और Edge 91 से उपलब्ध है. हालांकि, इसमें स्टैंडर्ड shadowrootmode
एट्रिब्यूट के बजाय, shadowroot
नाम के पुराने नॉन-स्टैंडर्ड एट्रिब्यूट का इस्तेमाल किया गया है. नया shadowrootmode
एट्रिब्यूट और स्ट्रीमिंग का तरीका, Chrome 111 और Edge 111 में उपलब्ध है.
यह वेब प्लैटफ़ॉर्म के नए एपीआई के तौर पर, एलान वाला शैडो DOM सभी ब्राउज़र पर अब तक बड़े पैमाने पर काम नहीं करता है. ब्राउज़र के साथ काम करने का पता लगाने के लिए, HTMLTemplateElement
के प्रोटोटाइप में shadowRootMode
प्रॉपर्टी की मौजूदगी का पता लगाएं:
function supportsDeclarativeShadowDOM() {
return HTMLTemplateElement.prototype.hasOwnProperty('shadowRootMode');
}
पॉलीफ़िल
डिक्लेरेटिव शैडो डीओएम के लिए, आसान पॉलीफ़िल बनाना काफ़ी आसान है. इसकी वजह यह है कि पॉलीफ़िल को टाइम सिमेंटिक्स या सिर्फ़ पार्सर वाली विशेषताओं की कॉपी बनाने की ज़रूरत नहीं है, जो ब्राउज़र को लागू करने पर लागू होती हैं. पॉलीफ़िल डिक्लेरेटिव शैडो DOM को स्कैन करके, सभी <template shadowrootmode>
एलिमेंट ढूंढे जा सकते हैं. इसके बाद, उन्हें पैरंट एलिमेंट पर अटैच किए गए शैडो रूट में बदला जा सकता है. दस्तावेज़ तैयार होने पर या कस्टम एलिमेंट की लाइफ़साइकल जैसे कुछ खास इवेंट से ट्रिगर होने पर, यह प्रोसेस पूरी की जा सकती है.
(function attachShadowRoots(root) {
root.querySelectorAll("template[shadowrootmode]").forEach(template => {
const mode = template.getAttribute("shadowrootmode");
const shadowRoot = template.parentNode.attachShadow({ mode });
shadowRoot.appendChild(template.content);
template.remove();
attachShadowRoots(shadowRoot);
});
})(document);