सीएसएस और स्टाइलिंग
इस लेख में, शैडो डीओएम की मदद से की जा सकने वाली अन्य शानदार चीज़ों के बारे में ज़्यादा जानकारी दी गई है. यह शैडो DOM 101 में बताए गए कॉन्सेप्ट पर आधारित है. अगर आपको इस बारे में जानकारी चाहिए, तो वह लेख पढ़ें.
परिचय
आइए, इस बात को स्वीकार करें. बिना स्टाइल वाले मार्कअप में कुछ भी आकर्षक नहीं होता. खुशकिस्मती से, वेब कॉम्पोनेंट बनाने वाले शानदार लोगों ने इस समस्या का अंदाज़ा पहले ही लगा लिया था और हमें अधर में नहीं छोड़ा. सीएसएस स्कोपिंग मॉड्यूल, शैडो ट्री में कॉन्टेंट को स्टाइल करने के लिए कई विकल्प तय करता है.
स्टाइल एनकैप्सुलेशन
शैडो डीओएम की मुख्य सुविधाओं में से एक है, शैडो बाउंड्री. इसमें कई अच्छी प्रॉपर्टी होती हैं. हालांकि, इसकी सबसे अच्छी प्रॉपर्टी यह है कि इसमें स्टाइल एनकैप्सुलेशन की सुविधा मुफ़्त में मिलती है. इसे दूसरे शब्दों में इस तरह कहा जा सकता है:
<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 सिलेक्टर से मैच करने वाला और लाल रंग में स्टाइल किया गया सिर्फ़ एक 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 के कॉन्टेक्स्ट में काम करता है. इसलिए, इसका इस्तेमाल शैडो DOM के बाहर नहीं किया जा सकता.
:host(<selector>) के फ़ंक्शनल फ़ॉर्म की मदद से, होस्ट एलिमेंट को टारगेट किया जा सकता है. हालांकि, इसके लिए ज़रूरी है कि वह <selector> से मैच करता हो.
उदाहरण - सिर्फ़ तब मैच करें, जब एलिमेंट में ही क्लास .different हो (उदाहरण के लिए, <x-foo class="different"></x-foo>):
:host(.different) {
...
}
उपयोगकर्ता की स्थितियों पर प्रतिक्रिया देना
:host का इस्तेमाल आम तौर पर तब किया जाता है, जब कोई कस्टम एलिमेंट बनाया जा रहा हो और आपको उपयोगकर्ता की अलग-अलग स्थितियों (:hover, :focus, :active वगैरह) पर प्रतिक्रिया देनी हो.
<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;
}
इससे आपको किसी एलिमेंट के शैडो DOM में स्टाइल के नियमों को शामिल करने की सुविधा मिलती है. इससे, एलिमेंट को उसके कॉन्टेक्स्ट के आधार पर यूनीक स्टाइल दिया जा सकता है.
एक शैडो रूट से कई तरह के होस्ट टाइप के लिए सहायता
:host का एक और इस्तेमाल तब किया जाता है, जब आपको थीमिंग लाइब्रेरी बनानी हो और एक ही शैडो डीओएम से कई तरह के होस्ट एलिमेंट को स्टाइल करना हो.
: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/ कॉम्बिनेटर, सीएसएस अथॉरिटी की वोर्पल तलवार की तरह हैं.
इनकी मदद से, शैडो डीओएम की सीमा को पार करके, शैडो ट्री में मौजूद एलिमेंट को स्टाइल किया जा सकता है.
::shadow स्यूडो-एलिमेंट
अगर किसी एलिमेंट में कम से कम एक शैडो ट्री है, तो ::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> के शैडो डीओएम में <x-panel> चाइल्ड एलिमेंट हैं. हर पैनल का अपना शैडो ट्री होता है. इसमें h2 हेडिंग होती हैं. मुख्य पेज से उन हेडिंग को स्टाइल करने के लिए, कोई व्यक्ति यह लिख सकता है:
x-tabs::shadow x-panel::shadow h2 {
...
}
/deep/ कॉम्बिनेटर
/deep/ कॉम्बिनेटर, ::shadow की तरह ही है, लेकिन ज़्यादा असरदार है. यह शैडो बाउंड्री को पूरी तरह से अनदेखा करता है और शैडो ट्री को पार कर जाता है. आसान शब्दों में कहें, तो /deep/ की मदद से किसी एलिमेंट के अंदरूनी हिस्से में जाकर, किसी भी नोड को टारगेट किया जा सकता है.
/deep/ कॉम्बिनेटर, कस्टम एलिमेंट की दुनिया में काफ़ी मददगार होता है. यहां शैडो डीओएम के कई लेवल होना आम बात है. इसके मुख्य उदाहरण हैं, कस्टम एलिमेंट के बंच को नेस्ट करना (हर एक एलिमेंट अपना शैडो ट्री होस्ट करता है) या <shadow> का इस्तेमाल करके, किसी दूसरे एलिमेंट से इनहेरिट करने वाला एलिमेंट बनाना.
उदाहरण (कस्टम एलिमेंट) - ट्री में कहीं भी मौजूद, <x-tabs> के डिसेंडेंट वाले सभी <x-panel> एलिमेंट चुनें:
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/ की मदद से, वेब प्लैटफ़ॉर्म पर मौजूद किसी भी ऐसे एलिमेंट को स्टाइल किया जा सकता है जो शैडो डीओएम का इस्तेमाल करता है. इसके बेहतरीन उदाहरण <input> टाइप और <video> हैं:
video /deep/ input[type="range"] {
background: hotpink;
}
स्टाइल हुक बनाना
पसंद के मुताबिक बनाने की सुविधा अच्छी है. कुछ मामलों में, हो सकता है कि आपको शैडो के स्टाइलिंग शील्ड में छेद करने हों और दूसरों के लिए स्टाइल बनाने के लिए हुक बनाने हों.
::shadow और /deep/ का इस्तेमाल करना
/deep/ में बहुत ताकत है. इससे कॉम्पोनेंट बनाने वाले लोगों को, अलग-अलग एलिमेंट को स्टाइल करने लायक या कई एलिमेंट को थीम के हिसाब से बनाने का तरीका मिलता है.
उदाहरण - सभी शैडो ट्री को अनदेखा करते हुए, .library-theme क्लास वाले सभी एलिमेंट को स्टाइल करें:
body /deep/ .library-theme {
...
}
कस्टम स्यूडो एलिमेंट का इस्तेमाल करना
WebKit और Firefox, दोनों ही नेटिव ब्राउज़र एलिमेंट के अंदरूनी हिस्सों को स्टाइल करने के लिए, सूडो एलिमेंट तय करते हैं. इसका एक अच्छा उदाहरण
input[type=range] है. स्लाइडर थंब <span style="color:blue">blue</span> को स्टाइल करने के लिए, ::-webkit-slider-thumb को टारगेट करें:
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>
सीएसएस वैरिएबल का इस्तेमाल करना
थीमिंग हुक बनाने का एक बेहतरीन तरीका, सीएसएस वैरिएबल का इस्तेमाल करना है. इसका मतलब है कि अन्य उपयोगकर्ताओं के लिए "स्टाइल प्लेसहोल्डर" बनाना, ताकि वे उन्हें भर सकें.
मान लें कि पसंद के मुताबिक बनाए गए एलिमेंट का कोई लेखक, अपने शैडो डीओएम में वैरिएबल प्लेसहोल्डर को मार्क करता है. एक इंटरनल बटन के फ़ॉन्ट को स्टाइल करने के लिए और दूसरा उसके रंग के लिए:
button {
color: var(--button-text-color, pink); /* default color will be pink */
font-family: var(--button-font);
}
इसके बाद, एलिमेंट को एम्बेड करने वाला व्यक्ति, अपनी पसंद के मुताबिक उन वैल्यू को तय करता है. शायद अपने पेज की शानदार कॉमिक सैंस थीम से मैच करने के लिए:
#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>
स्टाइल रीसेट की जा रही हैं
फ़ॉन्ट, रंग, और लाइन-हाइट जैसी इनहेरिट की जा सकने वाली स्टाइल, शैडो डीओएम में मौजूद एलिमेंट पर असर डालती रहती हैं. हालांकि, ज़्यादा से ज़्यादा फ़ायदा पाने के लिए, शैडो डीओएम हमें resetStyleInheritance प्रॉपर्टी देता है. इससे शैडो बाउंड्री पर होने वाली गतिविधियों को कंट्रोल किया जा सकता है.
इसे नया कॉम्पोनेंट बनाते समय, नए सिरे से शुरुआत करने का एक तरीका मानें.
resetStyleInheritance
false- डिफ़ॉल्ट. इनहेरिट की जा सकने वाली सीएसएस प्रॉपर्टी इनहेरिट होती रहती हैं.true- यह बाउंड्री पर, इनहेरिट की जा सकने वाली प्रॉपर्टी कोinitialपर रीसेट करता है.
यहां एक डेमो दिया गया है, जिसमें दिखाया गया है कि 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>
.resetStyleInheritance को समझना थोड़ा मुश्किल है. इसकी मुख्य वजह यह है कि यह सिर्फ़ उन सीएसएस प्रॉपर्टी पर असर डालता है जिन्हें इनहेरिट किया जा सकता है. इसका मतलब है कि जब आपको इनहेरिट करने के लिए कोई प्रॉपर्टी ढूंढनी हो, तो पेज और ShadowRoot के बीच की बाउंड्री पर, होस्ट से वैल्यू इनहेरिट न करें. इसके बजाय, सीएसएस स्पेसिफ़िकेशन के मुताबिक initial वैल्यू का इस्तेमाल करें.
अगर आपको नहीं पता कि सीएसएस में कौनसी प्रॉपर्टी इनहेरिट होती हैं, तो यह सूची देखें या एलिमेंट पैनल में "इनहेरिट की गई प्रॉपर्टी दिखाएं" चेकबॉक्स को टॉगल करें.
डिस्ट्रिब्यूट किए गए नोड की स्टाइलिंग
डिस्ट्रिब्यूटेड नोड ऐसे एलिमेंट होते हैं जो इंसर्शन पॉइंट (<content> एलिमेंट) पर रेंडर होते हैं. <content> एलिमेंट की मदद से, लाइट डीओएम से नोड चुने जा सकते हैं और उन्हें शैडो डीओएम में पहले से तय की गई जगहों पर रेंडर किया जा सकता है. ये लॉजिक के हिसाब से शैडो डीओएम में नहीं होते हैं. ये अब भी होस्ट एलिमेंट के चाइल्ड एलिमेंट होते हैं. इंसर्शन पॉइंट सिर्फ़ रेंडरिंग के लिए होते हैं.
डिस्ट्रिब्यूट किए गए नोड, मुख्य दस्तावेज़ की स्टाइल को बनाए रखते हैं. इसका मतलब है कि मुख्य पेज के स्टाइल नियम, एलिमेंट पर लागू होते रहते हैं. भले ही, वे इंसर्शन पॉइंट पर रेंडर हों. डिस्ट्रिब्यूट किए गए नोड अब भी लॉजिकल तौर पर लाइट DOM में होते हैं और अपनी जगह से नहीं हटते. वे बस कहीं और रेंडर होते हैं. हालांकि, जब नोड को शैडो डीओएम में डिस्ट्रिब्यूट किया जाता है, तो वे शैडो ट्री में तय की गई अतिरिक्त स्टाइल ले सकते हैं.
::content pseudo element
डिस्ट्रिब्यूट किए गए नोड, होस्ट एलिमेंट के चाइल्ड होते हैं. इसलिए, हम शैडो डीओएम के अंदर से उन्हें कैसे टारगेट कर सकते हैं? इसका जवाब है, सीएसएस ::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पर सेट किया जाता है. इस जगह को निचली सीमा कहा जाता है.
नतीजा
कस्टम एलिमेंट के लेखक के तौर पर, हमारे पास अपने कॉन्टेंट के लुक और स्टाइल को कंट्रोल करने के लिए कई विकल्प होते हैं. शैडो डीओएम, इस नई दुनिया की बुनियाद है.
शैडो डीओएम, हमें स्कोप स्टाइल एनकैप्सुलेशन देता है. साथ ही, यह हमें अपनी पसंद के मुताबिक, बाहरी दुनिया को ज़्यादा या कम दिखाने का ज़रिया देता है. कस्टम स्यूडो एलिमेंट तय करके या सीएसएस वैरिएबल प्लेसहोल्डर शामिल करके, लेखक तीसरे पक्षों को स्टाइलिंग हुक उपलब्ध करा सकते हैं. इससे, वे अपने कॉन्टेंट को और बेहतर तरीके से कस्टमाइज़ कर सकते हैं. कुल मिलाकर, वेब लेखकों के पास अपने कॉन्टेंट को दिखाने के तरीके का पूरा कंट्रोल होता है.