शैडो DOM 201

सीएसएस और स्टाइलिंग

इस लेख में, Shadow DOM की मदद से किए जा सकने वाले कुछ शानदार कामों के बारे में बताया गया है. यह, Shadow DOM 101 में बताए गए सिद्धांतों पर आधारित है. अगर आपको इस बारे में जानकारी चाहिए, तो वह लेख पढ़ें.

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

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

स्टाइल एनकैप्सुलेशन

शैडो DOM की एक मुख्य सुविधा शैडो बाउंड्री है. इसमें बहुत अच्छी प्रॉपर्टी हैं, लेकिन सबसे अच्छी बात यह है कि इसमें बिना किसी शुल्क के स्टाइल एनकैप्सुलेशन की सुविधा भी है. दूसरे तरीके से बताया गया:

<div><h3>Light DOM</h3></div>
<script>
var root = document.querySelector('div').createShadowRoot();
root.innerHTML = `
  <style>
    h3 {
      color: red;
    }
  </style>
  <h3>Shadow DOM</h3>
`;
</script>

इस डेमो के बारे में दो दिलचस्प बातें हैं:

  • इस पेज पर और भी h3s मौजूद हैं, लेकिन h3 सिलेक्टर से मैच करने वाला सिर्फ़ वही है जो पसंद के हिसाब से लाल है, वह ShadowRoot में है. फिर से, डिफ़ॉल्ट रूप से स्कोप वाली स्टाइल.
  • इस पेज पर बताए गए h3s को टारगेट करने वाले स्टाइल के अन्य नियमों को, मेरे कॉन्टेंट पर नहीं भेजा जाता. ऐसा इसलिए होता है, क्योंकि चुनने वाले लोग परछाई सीमा को पार नहीं करते.

कहानी का नैतिक रूप? हम बाहरी दुनिया को ध्यान में रखते हुए, कुछ अलग-अलग शैलियों को ध्यान में रखकर वीडियो बनाते हैं. धन्यवाद शैडो DOM!

होस्ट एलिमेंट का लुक तय करना

:host की मदद से, शैडो ट्री को होस्ट करने वाले एलिमेंट को चुना जा सकता है और उसे स्टाइल दिया जा सकता है:

<button class="red">My Button</button>
<script>
var button = document.querySelector('button');
var root = button.createShadowRoot();
root.innerHTML = `
  <style>
    :host {
      text-transform: uppercase;
    }
  </style>
  <content></content>
`;
</script>

एक मुमकिन है कि पैरंट पेज में मौजूद नियमों की खासियत, एलिमेंट में बताए गए :host नियमों से ज़्यादा हो. हालांकि, होस्ट एलिमेंट पर दिए गए style एट्रिब्यूट की तुलना में, इन नियमों की खासियत कम होती है. इससे उपयोगकर्ता, स्टाइल को बाहर से बदल सकते हैं. :host भी सिर्फ़ ShadowRoot के मामले में काम करता है, इसलिए इसका इस्तेमाल Shadow DOM के बाहर नहीं किया जा सकता.

:host(<selector>) के फ़ंक्शनल फ़ॉर्म की मदद से, होस्ट एलिमेंट को <selector> से मैच होने पर उसे टारगेट किया जा सकता है.

उदाहरण - सिर्फ़ तब मैच करें, जब एलिमेंट में .different क्लास हो (उदाहरण के लिए, <x-foo class="different"></x-foo>):

:host(.different) {
    ...
}

उपयोगकर्ता की स्थितियों पर प्रतिक्रिया देना

:host के इस्तेमाल का एक सामान्य उदाहरण यह है कि जब कोई कस्टम एलिमेंट बनाया जा रहा हो और आपको उपयोगकर्ता की अलग-अलग स्थितियों (:होवर, :फ़ोकस, :ऐक्टिव वगैरह) पर प्रतिक्रिया देनी हो.

<style>
  :host {
    opacity: 0.4;
    transition: opacity 420ms ease-in-out;
  }
  :host(:hover) {
    opacity: 1;
  }
  :host(:active) {
    position: relative;
    top: 3px;
    left: 3px;
  }
</style>

किसी एलिमेंट की थीम बनाना

अगर :host-context(<selector>) स्युडो क्लास, होस्ट एलिमेंट या इसके किसी ऐन्सेस्टर का <selector> से मेल खाता है, तो यह होस्ट एलिमेंट से मैच करेगी.

आम तौर पर, :host-context() का इस्तेमाल, किसी एलिमेंट की थीम उसके आस-पास के आधार पर करने के लिए किया जाता है. उदाहरण के लिए, कई लोग <html> या <body> पर क्लास लागू करके थीम बनाते हैं:

<body class="different">
  <x-foo></x-foo>
</body>

जब <x-foo>, .different क्लास वाले किसी एलिमेंट का डिसेंडेंट है, तो उसे :host-context(.different) को स्टाइल किया जा सकता है:

:host-context(.different) {
  color: red;
}

इससे एलिमेंट के शैडो डीओएम में स्टाइल के नियमों को इनकैप्सुलेट किया जा सकता है, जो कॉन्टेक्स्ट के आधार पर उन्हें स्टाइल देते हैं.

एक ही शैडो रूट का इस्तेमाल करके, कई तरह के होस्ट के साथ काम करता है

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

:host(x-foo) {
    /* Applies if the host is a <x-foo> element.*/
}

:host(x-foo:host) {
    /* Same as above. Applies if the host is a <x-foo> element. */
}

:host(div) {
    /* Applies if the host element is a <div>. */
}

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

::shadow स्युडो-एलिमेंट और /deep/ कॉमबिनेटर, सीएसएस अथॉरिटी की Vorpal तलवार होने की तरह हैं. इनकी मदद से, शैडो ट्री की थीम में शैडो डीओएम की बाउंड्री से पियर्सिंग की जा सकती है और एलिमेंट को स्टाइल किया जा सकता है.

::शैडो सूडो-एलिमेंट

अगर किसी एलिमेंट में कम से कम एक शैडो ट्री है, तो ::shadow स्यूडो एलिमेंट, शैडो रूट से मैच करता है. इससे आपको ऐसे सिलेक्टर लिखने की सुविधा मिलती है जो किसी एलिमेंट के शैडो डोम में इंटरनल नोड को स्टाइल करते हैं.

उदाहरण के लिए, अगर कोई एलिमेंट शैडो रूट को होस्ट कर रहा है, तो उसके शैडो ट्री के अंदर सभी स्पैन को स्टाइल देने के लिए #host::shadow span {} लिखा जा सकता है.

<style>
  #host::shadow span {
    color: red;
  }
</style>

<div id="host">
  <span>Light DOM</span>
</div>

<script>
  var host = document.querySelector('div');
  var root = host.createShadowRoot();
  root.innerHTML = `
    <span>Shadow DOM</span>
    <content></content>
  `;
</script>

उदाहरण (कस्टम एलिमेंट) - <x-tabs> के शैडो DOM में <x-panel> चाइल्ड हैं. हर पैनल अपने शैडो ट्री को होस्ट करता है जिसमें h2 हेडिंग होती हैं. मुख्य पेज से उन हेडिंग को स्टाइल देने के लिए, यह लिखा जा सकता है:

x-tabs::shadow x-panel::shadow h2 {
    ...
}

/deep/ कॉम्बिनेशन

/deep/ का संयोजक ::shadow के समान है, लेकिन ज़्यादा शक्तिशाली है. यह सभी परछाई सीमाओं को पूरी तरह से अनदेखा करता है और अनगिनत शैडो पेड़ों को पार करता है. आसान शब्दों में कहें, तो /deep/ की मदद से किसी एलिमेंट में मौजूद गड़बड़ी को ठीक से समझा जा सकता है और किसी भी नोड को टारगेट किया जा सकता है.

/deep/ कॉमबिनेटर, कस्टम एलिमेंट की दुनिया में खास तौर पर काम का है, जहां शैडो DOM के कई लेवल होना आम बात है. प्राइम उदाहरणों में कस्टम एलिमेंट शामिल करना शामिल है, जो हर एलिमेंट अपने शैडो ट्री को होस्ट करते हैं. इसके अलावा, इसमें <shadow> का इस्तेमाल करके ऐसा एलिमेंट बनाना भी शामिल है जो दूसरे एलिमेंट से इनहेरिट करता है.

उदाहरण (कस्टम एलिमेंट) - वे सभी <x-panel> एलिमेंट चुनें जो ट्री में कहीं भी <x-tabs> के डिसेंडेंट हैं:

x-tabs /deep/ x-panel {
    ...
}

उदाहरण - शैडो ट्री में कहीं भी .library-theme क्लास वाले सभी एलिमेंट को स्टाइल करें:

body /deep/ .library-theme {
    ...
}

querySelector() के साथ काम करना

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

// No fun.
document.querySelector('x-tabs').shadowRoot
        .querySelector('x-panel').shadowRoot
        .querySelector('#foo');

// Fun.
document.querySelector('x-tabs::shadow x-panel::shadow #foo');

नेटिव एलिमेंट का स्टाइल

नेटिव एचटीएमएल कंट्रोल को स्टाइल करना काफ़ी मुश्किल काम है. बहुत से लोग बस हार मान लेते हैं और अपने ही रोल आउट कर लेते हैं. हालांकि, ::shadow और /deep/ की मदद से, वेब प्लैटफ़ॉर्म पर शैडो DOM का इस्तेमाल करने वाले किसी भी एलिमेंट को स्टाइल किया जा सकता है. <input> और <video> टाइप के सबसे अच्छे उदाहरण हैं:

video /deep/ input[type="range"] {
  background: hotpink;
}

अलग-अलग स्टाइल के हुक बनाना

कस्टमाइज़ेशन अच्छा है. कुछ मामलों में, हो सकता है कि आप अपनी शैडो की स्टाइलिंग शील्ड में छेद करना चाहें और दूसरों को स्टाइल देने के लिए हुक बनाना चाहें.

::शैडो और /deep/ का इस्तेमाल करना

/deep/ के पीछे काफ़ी पावर है. यह कॉम्पोनेंट लेखकों को अलग-अलग एलिमेंट को स्टाइलेबल या कई एलिमेंट को थीमेबल के तौर पर दिखाने का तरीका देता है.

उदाहरण - सभी शैडो ट्री को अनदेखा करते हुए, क्लास .library-theme वाले सभी एलिमेंट को स्टाइल करें:

body /deep/ .library-theme {
    ...
}

कस्टम सूडो एलिमेंट का इस्तेमाल करना

WebKit और Firefox, दोनों ही नेटिव ब्राउज़र एलिमेंट के अंदरूनी हिस्सों को स्टाइल देने के लिए pseudo एलिमेंट के बारे में बताते हैं. एक अच्छा उदाहरण input[type=range] है. ::-webkit-slider-thumb को टारगेट करके, स्लाइडर थंब <span style="color:blue">blue</span> को स्टाइल किया जा सकता है:

input[type=range].custom::-webkit-slider-thumb {
  -webkit-appearance: none;
  background-color: blue;
  width: 10px;
  height: 40px;
}

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

pseudo एट्रिब्यूट का इस्तेमाल करके, किसी एलिमेंट को कस्टम सूडो एलिमेंट के तौर पर सेट किया जा सकता है. इसकी वैल्यू या नाम के आगे "x-" होना चाहिए. ऐसा करने से, शैडो ट्री में उस एलिमेंट के साथ एक असोसिएशन बन जाता है. साथ ही, बाहरी लोगों को शैडो की सीमा पार करने के लिए एक सही लेन मिल जाती है.

यहां कस्टम स्लाइडर विजेट बनाने और किसी व्यक्ति के स्लाइडर को अंगूठे के नीले रंग की स्टाइल में बदलने का उदाहरण दिया गया है:

<style>
  #host::x-slider-thumb {
    background-color: blue;
  }
</style>
<div id="host"></div>
<script>
  var root = document.querySelector('#host').createShadowRoot();
  root.innerHTML = `
    <div>
      <div pseudo="x-slider-thumb"></div>' +
    </div>
  `;
</script>

सीएसएस वैरिएबल का इस्तेमाल करना

थीम हुक बनाने का एक असरदार तरीका, सीएसएस वैरिएबल का इस्तेमाल करना है. ज़रूरी है कि "स्टाइल प्लेसहोल्डर" बनाएं, ताकि लोग इसे आसानी से भर सकें.

ऐसे कस्टम एलिमेंट के लेखक के बारे में सोचें जो अपने शैडो DOM में वैरिएबल प्लेसहोल्डर को मार्क करता है. एक आंतरिक बटन के फ़ॉन्ट को शैली देने के लिए और दूसरा उसके रंग के लिए:

button {
  color: var(--button-text-color, pink); /* default color will be pink */
  font-family: var(--button-font);
}

इसके बाद, एलिमेंट का एम्बेडर उन वैल्यू को उनके हिसाब से तय करता है. शायद उनके अपने पेज की सुपर शानदार Comic Sans थीम के साथ ऐसा करने के लिए:

#host {
  --button-text-color: green;
  --button-font: "Comic Sans MS", "Comic Sans", cursive;
}

सीएसएस वैरिएबल के इनहेरिट करने के तरीके की वजह से, हर चीज़ आकर्षक है और यह बहुत अच्छे से काम करता है! पूरी तस्वीर कुछ इस तरह दिखती है:

<style>
  #host {
    --button-text-color: green;
    --button-font: "Comic Sans MS", "Comic Sans", cursive;
  }
</style>
<div id="host">Host node</div>
<script>
  var root = document.querySelector('#host').createShadowRoot();
  root.innerHTML = `
    <style>
      button {
        color: var(--button-text-color, pink);
        font-family: var(--button-font);
      }
    </style>
    <content></content>
  `;
</script>

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

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

resetStyleInheritance

नीचे एक डेमो दिया गया है, जिसमें यह दिखाया गया है कि resetStyleInheritance को बदलने से शैडो ट्री पर क्या असर पड़ता है:

<div>
  <h3>Light DOM</h3>
</div>

<script>
  var root = document.querySelector('div').createShadowRoot();
  root.resetStyleInheritance = <span id="code-resetStyleInheritance">false</span>;
  root.innerHTML = `
    <style>
      h3 {
        color: red;
      }
    </style>
    <h3>Shadow DOM</h3>
    <content select="h3"></content>
  `;
</script>

<div class="demoarea" style="width:225px;">
  <div id="style-ex-inheritance"><h3 class="border">Light DOM</div>
</div>
<div id="inherit-buttons">
  <button id="demo-resetStyleInheritance">resetStyleInheritance=false</button>
</div>

<script>
  var container = document.querySelector('#style-ex-inheritance');
  var root = container.createShadowRoot();
  //root.resetStyleInheritance = false;
  root.innerHTML = '<style>h3{ color: red; }</style><h3>Shadow DOM<content select="h3"></content>';

  document.querySelector('#demo-resetStyleInheritance').addEventListener('click', function(e) {
    root.resetStyleInheritance = !root.resetStyleInheritance;
    e.target.textContent = 'resetStyleInheritance=' + root.resetStyleInheritance;
    document.querySelector('#code-resetStyleInheritance').textContent = root.resetStyleInheritance;
  });
</script>
DevTools से इनहेरिट की गई प्रॉपर्टी

.resetStyleInheritance को समझना थोड़ा मुश्किल है. खास तौर पर इसलिए, क्योंकि इसका असर सिर्फ़ उन सीएसएस प्रॉपर्टी पर पड़ता है जो इनहेरिट की जा सकती हैं. इसमें कहा गया है: जब आपको किसी प्रॉपर्टी को इनहेरिट करने की खोज करनी हो, तो पेज और ShadowRoot के बीच की सीमा पर इन वैल्यू को होस्ट से इनहेरिट न करें, बल्कि सीएसएस की खास जानकारी के हिसाब से, initial वैल्यू का इस्तेमाल करें.

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

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

डिस्ट्रिब्यूट किए गए नोड ऐसे एलिमेंट होते हैं जो इंसर्शन पॉइंट (<content> एलिमेंट) पर रेंडर होते हैं. <content> एलिमेंट की मदद से, लाइट डीओएम से नोड चुने जा सकते हैं. साथ ही, उन्हें अपने शैडो डीओएम में पहले से तय जगहों पर रेंडर किया जा सकता है. वे शैडो DOM में सही तरीके से नहीं होते हैं; वे अब भी होस्ट एलिमेंट के चाइल्ड एलिमेंट हैं. इंसर्शन पॉइंट सिर्फ़ रेंडरिंग की एक चीज़ हैं.

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

::कॉन्टेंट का pseudo एलिमेंट

डिस्ट्रिब्यूट किए गए नोड, होस्ट एलिमेंट के चाइल्ड एलिमेंट होते हैं. इसलिए, हम इन्हें Shadow DOM में के अंदर कैसे टारगेट कर सकते हैं? इसका जवाब है, सीएसएस ::content स्यूडो एलिमेंट. यह ऐसे लाइट DOM नोड को टारगेट करने का तरीका है जो किसी इंसर्शन पॉइंट से होकर गुज़रते हैं. उदाहरण के लिए:

::content > h3 किसी इंसर्शन पॉइंट से गुजरने वाले किसी भी h3 टैग को स्टाइल करता है.

आइए, एक उदाहरण देखें:

<div>
  <h3>Light DOM</h3>
  <section>
    <div>I'm not underlined</div>
    <p>I'm underlined in Shadow DOM!</p>
  </section>
</div>

<script>
var div = document.querySelector('div');
var root = div.createShadowRoot();
root.innerHTML = `
  <style>
    h3 { color: red; }
      content[select="h3"]::content > h3 {
      color: green;
    }
    ::content section p {
      text-decoration: underline;
    }
  </style>
  <h3>Shadow DOM</h3>
  <content select="h3"></content>
  <content select="section"></content>
`;
</script>

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

ShadowRoot बनाते समय, आपके पास इनहेरिट की गई स्टाइल को रीसेट करने का विकल्प होता है. <content> और <shadow> इंसर्शन पॉइंट में भी यह विकल्प होता है. इन एलिमेंट का इस्तेमाल करते समय, JS में .resetStyleInheritance सेट करें या एलिमेंट पर बूलियन reset-style-inheritance एट्रिब्यूट का इस्तेमाल करें.

  • ShadowRoot या <shadow> इंसर्शन पॉइंट के लिए: reset-style-inheritance का मतलब है कि इनहेरिट की जा सकने वाली सीएसएस प्रॉपर्टी को होस्ट पर initial पर सेट किया जाता है. ऐसा आपके शैडो कॉन्टेंट को हिट करने से पहले किया जाता है. इस जगह को ऊपरी सीमा के तौर पर जाना जाता है.

  • <content> इंसर्शन पॉइंट के लिए: reset-style-inheritance का मतलब है कि होस्ट के चिल्ड्रन को इंसर्शन पॉइंट पर बांटने से पहले, इनहेरिट की जा सकने वाली सीएसएस प्रॉपर्टी को initial पर सेट किया जाता है. इस जगह को निचली सीमा के तौर पर जाना जाता है.

नतीजा

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

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