बेहतर कॉन्सेप्ट और डीओएम एपीआई
इस लेख में, शैडो डीओएम की मदद से की जा सकने वाली अन्य बेहतरीन चीज़ों के बारे में बताया गया है! यह शैडो डीओएम 101 और शैडो डीओएम 201 में बताए गए कॉन्सेप्ट पर आधारित है.
एक से ज़्यादा शैडो रूट का इस्तेमाल करना
अगर आपने कोई पार्टी होस्ट की है, तो सभी लोगों के एक ही कमरे में होने से घुटन हो सकती है. आपको लोगों के ग्रुप को कई रूम में बांटने का विकल्प चाहिए. शैडो डीओएम को होस्ट करने वाले एलिमेंट भी ऐसा कर सकते हैं. इसका मतलब है कि वे एक समय में एक से ज़्यादा शैडो रूट होस्ट कर सकते हैं.
आइए देखते हैं कि अगर हम किसी होस्ट में एक से ज़्यादा शैडो रूट अटैच करने की कोशिश करते हैं, तो क्या होता है:
<div id="example1">Light DOM</div>
<script>
var container = document.querySelector('#example1');
var root1 = container.createShadowRoot();
var root2 = container.createShadowRoot();
root1.innerHTML = '<div>Root 1 FTW</div>';
root2.innerHTML = '<div>Root 2 FTW</div>';
</script>
"Root 2 FTW" रेंडर होता है, भले ही हमने पहले से ही शैडो ट्री अटैच किया हो. ऐसा इसलिए होता है, क्योंकि होस्ट में जोड़ा गया आखिरी शैडो ट्री ही लागू होता है. जहां तक रेंडरिंग का सवाल है, यह एक LIFO स्टैक है. DevTools की जांच करने पर, इस व्यवहार की पुष्टि होती है.
तो फिर एक से ज़्यादा शैडो इस्तेमाल करने का क्या फ़ायदा, जब रेंडरिंग पार्टी में सिर्फ़ आखिरी शैडो को ही बुलाया जाता है? शैडो इंसर्शन पॉइंट डालें.
शैडो इंसर्शन पॉइंट
"शैडो इंसर्शन पॉइंट" (<shadow>), सामान्य इंसर्शन पॉइंट (<content>) की तरह ही होते हैं. ये प्लेसहोल्डर होते हैं. हालांकि, ये होस्ट के कॉन्टेंट के लिए प्लेसहोल्डर के तौर पर काम करने के बजाय, अन्य शैडो ट्री के लिए होस्ट के तौर पर काम करते हैं.
यह शैडो डीओएम इंसेप्शन है!
जैसा कि आप शायद कल्पना कर सकते हैं, जैसे-जैसे आप रैबिट होल में गहराई तक जाते हैं, चीज़ें और ज़्यादा जटिल होती जाती हैं. इस वजह से, स्पेसिफ़िकेशन में साफ़ तौर पर बताया गया है कि एक से ज़्यादा <shadow> एलिमेंट होने पर क्या होता है:
अपने मूल उदाहरण पर वापस लौटते हुए, पहले शैडो root1 को न्योते की सूची से बाहर कर दिया गया. <shadow> इंसर्शन पॉइंट जोड़ने पर, यह वापस आ जाता है:
<div id="example2">Light DOM</div>
<script>
var container = document.querySelector('#example2');
var root1 = container.createShadowRoot();
var root2 = container.createShadowRoot();
root1.innerHTML = '<div>Root 1 FTW</div><content></content>';
**root2.innerHTML = '<div>Root 2 FTW</div><shadow></shadow>';**
</script>
इस उदाहरण में कुछ दिलचस्प बातें हैं:
- "Root 2 FTW" अब भी "Root 1 FTW" के ऊपर रेंडर होता है. ऐसा इसलिए होता है, क्योंकि हमने
<shadow>इंसर्शन पॉइंट को सही जगह पर रखा है. अगर आपको इसके उलट करना है, तो इंसर्शन पॉइंट को यहां ले जाएं:root2.innerHTML = '<shadow></shadow><div>Root 2 FTW</div>';. - ध्यान दें कि अब root1 में
<content>इंसर्शन पॉइंट है. इससे टेक्स्ट नोड "लाइट डीओएम" रेंडरिंग के लिए साथ आता है.
<shadow> पर क्या रेंडर किया गया है?
कभी-कभी, <shadow> पर रेंडर किए जा रहे पुराने शैडो ट्री के बारे में जानना फ़ायदेमंद होता है. .olderShadowRoot की मदद से, उस ट्री का रेफ़रंस पाया जा सकता है:
**root2.olderShadowRoot** === root1 //true
होस्ट का शैडो रूट पाना
अगर कोई एलिमेंट शैडो DOM को होस्ट कर रहा है, तो .shadowRoot का इस्तेमाल करके उसके सबसे नए शैडो रूट को ऐक्सेस किया जा सकता है:
var root = host.createShadowRoot();
console.log(host.shadowRoot === root); // true
console.log(document.body.shadowRoot); // null
अगर आपको लगता है कि लोग आपके शैडो में जा रहे हैं, तो .shadowRoot को शून्य के तौर पर फिर से तय करें:
Object.defineProperty(host, 'shadowRoot', {
get: function() { return null; },
set: function(value) { }
});
यह थोड़ा मुश्किल है, लेकिन काम करता है. आखिर में, यह याद रखना ज़रूरी है कि शैडो डीओएम एक शानदार सुविधा है, लेकिन इसे सुरक्षा से जुड़ी सुविधा के तौर पर डिज़ाइन नहीं किया गया है. कॉन्टेंट को पूरी तरह से अलग करने के लिए, इस पर भरोसा न करें.
JS में शैडो डीओएम बनाना
अगर आपको JS में DOM बनाना पसंद है, तो HTMLContentElement और HTMLShadowElement में इसके लिए इंटरफ़ेस मौजूद हैं.
<div id="example3">
<span>Light DOM</span>
</div>
<script>
var container = document.querySelector('#example3');
var root1 = container.createShadowRoot();
var root2 = container.createShadowRoot();
var div = document.createElement('div');
div.textContent = 'Root 1 FTW';
root1.appendChild(div);
// HTMLContentElement
var content = document.createElement('content');
content.select = 'span'; // selects any spans the host node contains
root1.appendChild(content);
var div = document.createElement('div');
div.textContent = 'Root 2 FTW';
root2.appendChild(div);
// HTMLShadowElement
var shadow = document.createElement('shadow');
root2.appendChild(shadow);
</script>
यह उदाहरण, पिछले सेक्शन में दिए गए उदाहरण से काफ़ी मिलता-जुलता है.
बस इतना फ़र्क़ है कि अब मैं select का इस्तेमाल करके, हाल ही में जोड़े गए <span> को निकाल रहा हूं.
इंसर्शन पॉइंट के साथ काम करना
होस्ट एलिमेंट से बाहर चुने गए और शैडो ट्री में "डिस्ट्रिब्यूट" किए गए नोड को…ड्रमरोल…डिस्ट्रिब्यूटेड नोड कहा जाता है! इन्हें शैडो बाउंड्री को पार करने की अनुमति होती है, जब इंसर्शन पॉइंट इन्हें अंदर आने का न्योता देते हैं.
इंसर्शन पॉइंट के बारे में सबसे अजीब बात यह है कि वे डीओएम को असल में मूव नहीं करते. होस्ट के नोड में कोई बदलाव नहीं होता. इंसर्शन पॉइंट, होस्ट से शैडो ट्री में नोड को फिर से प्रोजेक्ट करते हैं. यह प्रज़ेंटेशन/रेंडरिंग से जुड़ी चीज़ है: "इन नोड को यहां ले जाएं" "इन नोड को इस जगह पर रेंडर करें."
उदाहरण के लिए:
<div><h2>Light DOM</h2></div>
<script>
var root = document.querySelector('div').createShadowRoot();
root.innerHTML = '<content select="h2"></content>';
var h2 = document.querySelector('h2');
console.log(root.querySelector('content[select="h2"] h2')); // null;
console.log(root.querySelector('content').contains(h2)); // false
</script>
वाह! h2, शैडो डीओएम का चाइल्ड टैग नहीं है. इससे एक और बात पता चलती है:
Element.getDistributedNodes()
हम <content> में नहीं जा सकते, लेकिन .getDistributedNodes() एपीआई हमें इंसर्शन पॉइंट पर डिस्ट्रिब्यूट किए गए नोड के बारे में क्वेरी करने की अनुमति देता है:
<div id="example4">
<h2>Eric</h2>
<h2>Bidelman</h2>
<div>Digital Jedi</div>
<h4>footer text</h4>
</div>
<template id="sdom">
<header>
<content select="h2"></content>
</header>
<section>
<content select="div"></content>
</section>
<footer>
<content select="h4:first-of-type"></content>
</footer>
</template>
<script>
var container = document.querySelector('#example4');
var root = container.createShadowRoot();
var t = document.querySelector('#sdom');
var clone = document.importNode(t.content, true);
root.appendChild(clone);
var html = [];
[].forEach.call(root.querySelectorAll('content'), function(el) {
html.push(el.outerHTML + ': ');
var nodes = el.getDistributedNodes();
[].forEach.call(nodes, function(node) {
html.push(node.outerHTML);
});
html.push('\n');
});
</script>
Element.getDestinationInsertionPoints()
.getDistributedNodes() की तरह ही, यह देखा जा सकता है कि किसी नोड को किन इंसर्शन पॉइंट में बांटा गया है. इसके लिए, उसके .getDestinationInsertionPoints() को कॉल करें:
<div id="host">
<h2>Light DOM
</div>
<script>
var container = document.querySelector('div');
var root1 = container.createShadowRoot();
var root2 = container.createShadowRoot();
root1.innerHTML = '<content select="h2"></content>';
root2.innerHTML = '<shadow></shadow>';
var h2 = document.querySelector('#host h2');
var insertionPoints = h2.getDestinationInsertionPoints();
[].forEach.call(insertionPoints, function(contentEl) {
console.log(contentEl);
});
</script>
टूल: शैडो डीओएम विज़ुअलाइज़र
शैडो डीओएम को समझना मुश्किल है. मुझे याद है कि पहली बार इसे समझने में मुझे कितनी मुश्किल हुई थी.
शैडो डीओएम रेंडरिंग कैसे काम करती है, यह विज़ुअलाइज़ करने में मदद करने के लिए, मैंने d3.js का इस्तेमाल करके एक टूल बनाया है. बाईं ओर मौजूद दोनों मार्कअप बॉक्स में बदलाव किया जा सकता है. बेझिझक अपना मार्कअप चिपकाएं और देखें कि चीज़ें कैसे काम करती हैं. साथ ही, यह भी देखें कि इंसर्शन पॉइंट, होस्ट नोड को शैडो ट्री में कैसे स्वैप करते हैं.
इसे आज़माएं और हमें बताएं कि आपको यह कैसा लगा!
इवेंट मॉडल
कुछ इवेंट, शैडो बाउंड्री को पार करते हैं और कुछ नहीं. अगर इवेंट, बाउंड्री को पार करते हैं, तो इवेंट टारगेट में बदलाव किया जाता है. ऐसा इसलिए किया जाता है, ताकि शैडो रूट की ऊपरी बाउंड्री से मिलने वाले एनकैप्सुलेशन को बनाए रखा जा सके. इसका मतलब है कि इवेंट को फिर से टारगेट किया जाता है, ताकि वे शैडो डीओएम के इंटरनल एलिमेंट के बजाय होस्ट एलिमेंट से आए हुए लगें.
Play Action 1
- यह दिलचस्प है. आपको होस्ट एलिमेंट (
<div data-host>) से नीले नोड तकmouseoutदिखना चाहिए. भले ही यह डिस्ट्रिब्यूटेड नोड हो, लेकिन यह अब भी होस्ट में है, न कि ShadowDOM में. माउस को पीले रंग के हिस्से में और नीचे ले जाने पर, नीले रंग के नोड पर फिर सेmouseoutहोता है.
Play Action 2
- होस्ट पर एक
mouseoutदिखता है (सबसे आखिर में). आम तौर पर, आपको सभी पीले ब्लॉक के लिएmouseoutइवेंट ट्रिगर होते हुए दिखेंगे. हालांकि, इस मामले में ये एलिमेंट, शैडो डीओएम के अंदर मौजूद होते हैं और इवेंट अपनी ऊपरी सीमा से बाहर नहीं निकलता.
Play Action 3
- ध्यान दें कि इनपुट पर क्लिक करने पर,
focusinइनपुट पर नहीं, बल्कि होस्ट नोड पर दिखता है. इसे फिर से टारगेट किया गया है!
ऐसे इवेंट जो हमेशा रोके जाते हैं
ये इवेंट, शैडो बाउंड्री को कभी पार नहीं करते:
- रहने दो
- गड़बड़ी
- चुनें
- बदलें
- लोड
- रीसेट करें
- साइज़ बदलें
- स्क्रोल
- selectstart
नतीजा
हमें उम्मीद है कि आप इस बात से सहमत होंगे कि शैडो डीओएम काफ़ी असरदार है. पहली बार, हमारे पास <iframe> या अन्य पुरानी तकनीकों के अतिरिक्त बोझ के बिना, सही एनकैप्सुलेशन है.
शैडो डीओएम को समझना मुश्किल है, लेकिन यह वेब प्लैटफ़ॉर्म के लिए एक बेहतरीन टूल है. इसे थोड़ा समय दें. जानकारी पाएं. सवाल पूछें.
अगर आपको इस बारे में ज़्यादा जानना है, तो डॉमिनिक का लेख शैडो DOM 101 और मेरा लेख शैडो DOM 201: सीएसएस और स्टाइलिंग पढ़ें.