टेंप्लेट, स्लॉट, और शैडो

वेब कॉम्पोनेंट को फिर से इस्तेमाल करने का फ़ायदा यह है: यूज़र इंटरफ़ेस (यूआई) विजेट को एक बार बनाने के बाद, उसे कई बार इस्तेमाल किया जा सकता है. हालांकि, आपको वेब कॉम्पोनेंट बनाने के लिए JavaScript की ज़रूरत होती है, लेकिन इसके लिए आपको JavaScript लाइब्रेरी की ज़रूरत नहीं होती. एचटीएमएल और उससे जुड़े एपीआई आपकी ज़रूरत की हर चीज़ उपलब्ध कराते हैं.

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

इस सेक्शन में, हम <star-rating> एलिमेंट बनाएंगे. यह एक ऐसा वेब कॉम्पोनेंट है जिसकी मदद से उपयोगकर्ता किसी अनुभव को एक से पांच स्टार के पैमाने पर रेटिंग दे सकते हैं. कस्टम एलिमेंट को नाम देते समय, सभी अंग्रेज़ी के छोटे अक्षरों का इस्तेमाल करने का सुझाव दिया जाता है. साथ ही, डैश इस्तेमाल करें, क्योंकि इससे सामान्य और कस्टम एलिमेंट के बीच अंतर करने में मदद मिलती है.

इस सेशन में हम <template> और <slot> एलिमेंट, slot एट्रिब्यूट, और JavaScript का इस्तेमाल करके, एनकैप्सुलेट किए गए Shadow DOM का टेंप्लेट बनाने पर चर्चा करेंगे. इसके बाद हम टेक्स्ट के किसी सेक्शन को पसंद के मुताबिक बनाते हुए तय किए गए एलिमेंट का फिर से इस्तेमाल करेंगे, जैसा कि किसी भी एलिमेंट या वेब कॉम्पोनेंट को किया जाता है. हम कस्टम एलिमेंट के अंदर और बाहर, सीएसएस को इस्तेमाल करने के बारे में भी खास जानकारी देंगे.

<template> एलिमेंट

<template> एलिमेंट का इस्तेमाल, एचटीएमएल के फ़्रैगमेंट को क्लोन करने और JavaScript की मदद से डीओएम में शामिल करने का एलान करने के लिए किया जाता है. एलिमेंट का कॉन्टेंट, डिफ़ॉल्ट रूप से रेंडर नहीं किया जाता. इसके बजाय, उन्हें JavaScript का इस्तेमाल करके इंस्टैंशिएट किया जाता है.

<template id="star-rating-template">
  <form>
    <fieldset>
      <legend>Rate your experience:</legend>
      <rating>
        <input type="radio" name="rating" value="1" aria-label="1 star" required />
        <input type="radio" name="rating" value="2" aria-label="2 stars" />
        <input type="radio" name="rating" value="3" aria-label="3 stars" />
        <input type="radio" name="rating" value="4" aria-label="4 stars" />
        <input type="radio" name="rating" value="5" aria-label="5 stars" />
      </rating>
    </fieldset>
    <button type="reset">Reset</button>
    <button type="submit">Submit</button>
  </form>
</template>

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

इस उदाहरण में, <form>, DOM में किसी <template> का चाइल्ड नहीं है. <template> एलिमेंट का कॉन्टेंट, HTMLTemplateElement.content प्रॉपर्टी से लौटाए गए DocumentFragment के चाइल्ड एलिमेंट होते हैं. लोगों को दिखने के लिए, JavaScript का इस्तेमाल कॉन्टेंट को इकट्ठा करने और उन कॉन्टेंट को डीओएम में जोड़ने के लिए किया जाना चाहिए.

इस छोटे JavaScript ने कोई कस्टम एलिमेंट नहीं बनाया. इसके बजाय, इस उदाहरण ने <template> के कॉन्टेंट को <body> में जोड़ दिया है. कॉन्टेंट, दिखने वाले और स्टाइल किए जा सकने वाले DOM का हिस्सा बन गया है.

पिछले कोडपेन का स्क्रीनशॉट, जैसा कि डीओएम में दिखाया गया है.

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

<slot> एलिमेंट

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

अपने टेंप्लेट में, हम <legend> को <slot> में बदलते हैं:

<template id="star-rating-template">
  <form>
    <fieldset>
      <slot name="star-rating-legend">
        <legend>Rate your experience:</legend>
      </slot>

name एट्रिब्यूट का इस्तेमाल, दूसरे एलिमेंट को स्लॉट असाइन करने के लिए किया जाता है. ऐसा तब होता है, जब एलिमेंट में स्लॉट एट्रिब्यूट की वैल्यू, नाम वाले स्लॉट के नाम से मेल खाती हो. अगर कस्टम एलिमेंट में किसी स्लॉट से मेल नहीं खाता है, तो <slot> के कॉन्टेंट को रेंडर किया जाएगा. इसलिए, हमने जेनरिक कॉन्टेंट वाले <legend> को शामिल किया है. अगर कोई व्यक्ति बिना कॉन्टेंट वाले <star-rating></star-rating> को अपने एचटीएमएल में शामिल करता है, तो इसे रेंडर किया जा सकता है.

<star-rating>
  <legend slot="star-rating-legend">Blendan Smooth</legend>
</star-rating>
<star-rating>
  <legend slot="star-rating-legend">Hoover Sukhdeep</legend>
</star-rating>
<star-rating>
  <legend slot="star-rating-legend">Toasty McToastface</legend>
  <p>Is this text visible?</p>
</star-rating>

स्लॉट एट्रिब्यूट एक ग्लोबल एट्रिब्यूट है, जिसका इस्तेमाल <template> में <slot> के कॉन्टेंट को बदलने के लिए किया जाता है. हमारे कस्टम एलिमेंट में, स्लॉट एट्रिब्यूट वाला एलिमेंट एक <legend> है. ज़रूरी नहीं है. हमारे टेंप्लेट में, <slot name="star-rating-legend"> को <anyElement slot="star-rating-legend"> से बदल दिया जाएगा. इसमें <anyElement> कोई भी एलिमेंट हो सकता है, यहां तक कि कोई दूसरा कस्टम एलिमेंट भी.

ऐसे एलिमेंट जो तय नहीं किए गए हैं

हमने अपने <template> में, <rating> एलिमेंट का इस्तेमाल किया है. यह कोई कस्टम एलिमेंट नहीं है. दरअसल, यह एक अनजान एलिमेंट है. ब्राउज़र उस समय काम नहीं करते हैं, जब वे किसी एलिमेंट को नहीं पहचान पाते. पहचान में न आने वाले एचटीएमएल एलिमेंट को ब्राउज़र, बिना नाम वाले इनलाइन एलिमेंट के तौर पर देखता है. इन एलिमेंट को सीएसएस की मदद से भी स्टाइल किया जा सकता है. <span> की तरह ही, <rating> और <star-rating> एलिमेंट में उपयोगकर्ता एजेंट लागू नहीं किया गया स्टाइल या सिमैंटिक नहीं होता.

ध्यान दें कि <template> और कॉन्टेंट को रेंडर नहीं किया गया है. <template> एक ऐसा एलिमेंट है जिसमें मौजूद कॉन्टेंट को रेंडर नहीं किया जा सकता. <star-rating> एलिमेंट तय करना बाकी है. जब तक हम किसी एलिमेंट के बारे में नहीं बताते, तब तक ब्राउज़र उसे ऐसे सभी एलिमेंट की तरह ही दिखाता है जिनकी पहचान नहीं की जा सकती. फ़िलहाल, बिना पहचान वाले <star-rating> को पहचान छिपाने वाले इनलाइन एलिमेंट के तौर पर माना जाता है. इसलिए, तीसरे <star-rating> में लेजेंड और <p> जैसे कॉन्टेंट को ठीक वैसे ही दिखाया जाएगा जैसा <span> में होने पर दिखता था.

इस अनजान एलिमेंट को कस्टम एलिमेंट में बदलने के लिए, आइए अपने एलिमेंट को तय करें.

कस्टम एलिमेंट

कस्टम एलिमेंट तय करने के लिए JavaScript ज़रूरी है. तय किए जाने पर, <star-rating> एलिमेंट का कॉन्टेंट एक शैडो रूट से बदल दिया जाएगा. इसमें, उस टेंप्लेट का पूरा कॉन्टेंट मौजूद होगा जिसे हम उससे जोड़ते हैं. टेंप्लेट के <slot> एलिमेंट, <star-rating> में उस एलिमेंट के कॉन्टेंट से बदल दिए जाते हैं जिसका slot एट्रिब्यूट वैल्यू, <slot> की नाम वैल्यू से मेल खाता है. अगर ऐसा नहीं है, तो टेंप्लेट के स्लॉट का कॉन्टेंट दिखता है.

किसी कस्टम एलिमेंट में मौजूद कॉन्टेंट, जो किसी स्लॉट से नहीं जुड़ा है—हमारे तीसरे <star-rating> में मौजूद <p>Is this text visible?</p>—को शैडो रूट में शामिल नहीं किया गया है, इसलिए इसे दिखाया नहीं जाता है.

हम HTMLElement को बढ़ाकर star-rating नाम का कस्टम एलिमेंट तय करते हैं:

customElements.define('star-rating',
  class extends HTMLElement {
    constructor() {
      super(); // Always call super first in constructor
      const starRating = document.getElementById('star-rating-template').content;
      const shadowRoot = this.attachShadow({
        mode: 'open'
      });
      shadowRoot.appendChild(starRating.cloneNode(true));
    }
  });

अब एलिमेंट तय हो जाने पर, ब्राउज़र को जब भी कोई <star-rating> एलिमेंट मिलेगा, तब उसे हमारे टेंप्लेट वाले #star-rating-template एलिमेंट के मुताबिक रेंडर किया जाएगा. ब्राउज़र, नोड में शैडो डीओएम ट्री को अटैच करेगा और उस शैडो डीओएम में टेंप्लेट के कॉन्टेंट का क्लोन जोड़ देगा. ध्यान दें कि जिन एलिमेंट पर attachShadow() का इस्तेमाल किया जा सकता है वे सीमित हैं.

const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.appendChild(starRating.cloneNode(true));

डेवलपर टूल देखने पर, आपको यह पता चलेगा कि <template> में मौजूद <form>, हर कस्टम एलिमेंट के शैडो रूट का हिस्सा है. <template> के कॉन्टेंट का क्लोन, डेवलपर टूल के हर कस्टम एलिमेंट में साफ़ तौर पर दिखता है और ब्राउज़र पर भी दिखता है. हालांकि, कस्टम एलिमेंट के कॉन्टेंट को स्क्रीन पर रेंडर नहीं किया जाता.

DevTools का स्क्रीनशॉट, जिसमें हर कस्टम एलिमेंट में क्लोन किए गए टेंप्लेट का कॉन्टेंट दिखाया गया है.

<template> के उदाहरण में, हमने टेंप्लेट के कॉन्टेंट को दस्तावेज़ के मुख्य हिस्से में जोड़ दिया है. साथ ही, कॉन्टेंट को सामान्य DOM में जोड़ दिया है. customElements की परिभाषा में, हमने एक जैसे appendChild() का इस्तेमाल किया है. हालांकि, क्लोन किए गए टेंप्लेट के कॉन्टेंट को एनकैप्सुलेट किए गए शैडो डीओएम में जोड़ा गया है.

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

शैडो DOM

Shadow DOM, हर शैडो ट्री के लिए सीएसएस स्टाइल का स्कोप बनाता है और उसे दस्तावेज़ के बाकी हिस्सों से अलग कर देता है. इसका मतलब है कि बाहरी सीएसएस आपके कॉम्पोनेंट पर लागू नहीं होता. साथ ही, कॉम्पोनेंट स्टाइल का बाकी दस्तावेज़ पर तब तक कोई असर नहीं पड़ता, जब तक कि हम जान-बूझकर उन्हें इस पर न ले जाएं.

हमने कॉन्टेंट को शैडो डीओएम में जोड़ा है. इसलिए, हम कस्टम एलिमेंट में एनकैप्सुलेटेड सीएसएस देने वाला <style> एलिमेंट शामिल कर सकते हैं.

कस्टम एलिमेंट के दायरे में आने की वजह से, हमें दस्तावेज़ के बाकी हिस्से में दिखने वाली स्टाइल के बारे में चिंता करने की ज़रूरत नहीं है. हम सिलेक्टर की खासियत को काफ़ी हद तक कम कर सकते हैं. उदाहरण के लिए, कस्टम एलिमेंट में इस्तेमाल किए जाने वाले इनपुट सिर्फ़ रेडियो बटन होते हैं, इसलिए हम input[type="radio"] के बजाय input को सिलेक्टर के तौर पर इस्तेमाल कर सकते हैं.

 <template id="star-rating-template">
  <style>
    rating {
      display: inline-flex;
    }
    input {
      appearance: none;
      margin: 0;
      box-shadow: none;
    }
    input::after {
      content: '\2605'; /* solid star */
      font-size: 32px;
    }
    rating:hover input:invalid::after,
    rating:focus-within input:invalid::after {
      color: #888;
    }
    input:invalid::after,
      rating:hover input:hover ~ input:invalid::after,
      input:focus ~ input:invalid::after  {
      color: #ddd;
    }
    input:valid {
      color: orange;
    }
    input:checked ~ input:not(:checked)::after {
      color: #ccc;
      content: '\2606'; /* hollow star */
    }
  </style>
  <form>
    <fieldset>
      <slot name="star-rating-legend">
        <legend>Rate your experience:</legend>
      </slot>
      <rating>
        <input type="radio" name="rating" value="1" aria-label="1 star" required/>
        <input type="radio" name="rating" value="2" aria-label="2 stars"/>
        <input type="radio" name="rating" value="3" aria-label="3 stars"/>
        <input type="radio" name="rating" value="4" aria-label="4 stars"/>
        <input type="radio" name="rating" value="5" aria-label="5 stars"/>
      </rating>
    </fieldset>
    <button type="reset">Reset</button>
    <button type="submit">Submit</button>
  </form>
</template>

जहां वेब कॉम्पोनेंट को in-<template> मार्कअप और सीएसएस स्टाइल के साथ एन्कैप्सुलेट किया जाता है और सीएसएस स्टाइल, शैडो डीओएम तक सीमित होती हैं और कॉम्पोनेंट के बाहर की हर चीज़ से छिपा दी जाती हैं, जबकि रेंडर किए जाने वाले स्लॉट कॉन्टेंट, <anyElement slot="star-rating-legend"> के <star-rating> वाले हिस्से को एनकैप्सुलेट नहीं किया जाता है.

स्टाइल को मौजूदा दायरे से बाहर रखना

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

शैडो ट्री, शैडो डीओएम में मौजूद डीओएम ट्री होता है. शैडो रूट, शैडो ट्री का रूट नोड होता है.

:host स्यूडो-क्लास, शैडो होस्ट एलिमेंट <star-rating> को चुनता है. शैडो होस्ट वह DOM नोड है जिससे शैडो DOM को अटैच किया गया है. होस्ट के सिर्फ़ खास वर्शन को टारगेट करने के लिए, :host() का इस्तेमाल करें. इससे सिर्फ़ पास किए गए पैरामीटर से मैच करने वाले शैडो होस्ट एलिमेंट चुने जाएंगे, जैसे कि क्लास या एट्रिब्यूट सिलेक्टर. सभी कस्टम एलिमेंट चुनने के लिए, ग्लोबल सीएसएस में star-rating { /* styles */ } या टेंप्लेट स्टाइल में :host(:not(#nonExistantId)) का इस्तेमाल किया जा सकता है. खासियत के मामले में, ग्लोबल सीएसएस जीतता है.

::slotted() स्यूडो-एलिमेंट, शैडो डीओएम में शैडो की सीमा को पार करता है. सिलेक्टर से मेल खाने पर, यह किसी स्लॉट वाले एलिमेंट को चुनता है. हमारे उदाहरण में, ::slotted(legend) हमारे तीन लेजेंड से मेल खाता है.

ग्लोबल स्कोप में सीएसएस के शैडो डीओएम को टारगेट करने के लिए, टेंप्लेट में बदलाव करना ज़रूरी है. part एट्रिब्यूट को ऐसे किसी भी एलिमेंट में जोड़ा जा सकता है जिसे स्टाइल करना है. इसके बाद, पास किए गए पैरामीटर से मैच करने वाले शैडो ट्री के एलिमेंट को मैच करने के लिए, ::part() स्यूडो-एलिमेंट का इस्तेमाल करें. स्यूडो-एलिमेंट के लिए ऐंकर या शुरुआती एलिमेंट, होस्ट या कस्टम एलिमेंट का नाम होता है. उदाहरण के लिए, star-rating. पैरामीटर, part एट्रिब्यूट की वैल्यू होता है.

अगर हमारा टेंप्लेट मार्कअप इस तरह शुरू होता है:

<template id="star-rating-template">
  <form part="formPart">
    <fieldset part="fieldsetPart">

हम <form> और <fieldset> को इससे टारगेट कर सकते हैं:

star-rating::part(formPart) { /* styles */ }
star-rating::part(fieldsetPart) { /* styles */ }

पार्ट नाम, क्लास की तरह ही काम करते हैं: किसी एलिमेंट में स्पेस से अलग किए गए कई पार्ट नाम हो सकते हैं और एक से ज़्यादा एलिमेंट का एक ही हिस्सा नाम हो सकता है.

कस्टम एलिमेंट बनाने के लिए, Google के पास एक शानदार चेकलिस्ट है. डिक्लेरेटिव शैडो डीओएम के बारे में भी जानें.

आपने जो सीखा है उसकी जांच करें

टेंप्लेट, स्लॉट, और शैडो के बारे में अपनी जानकारी को टेस्ट करें.

शैडो DOM के बाहर की डिफ़ॉल्ट स्टाइल, अंदर के एलिमेंट को स्टाइल करेंगी.

सही.
फिर से कोशिश करें.
गलत.
सही जवाब!

<template> एलिमेंट का कौनसा जवाब सही है?

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