क्लाइंट-साइड टेंप्लेट को स्टैंडर्ड बनाना
परिचय
वेब डेवलपमेंट के लिए टेंप्लेट का कॉन्सेप्ट नया नहीं है. असल में, Django (Python), ERB/Haml (Ruby), और Smarty (PHP) जैसी सर्वर-साइड टेंप्लेट बनाने वाली भाषाएं/इंजन, लंबे समय से मौजूद हैं. हालांकि, पिछले कुछ सालों में, हमें एमवीसी फ़्रेमवर्क का ज़बरदस्त इस्तेमाल हुआ है. ये सभी टूल थोड़े अलग होते हैं. हालांकि, ज़्यादातर टूल में प्रज़ेंटेशन लेयर (जिसे डेटा व्यू भी कहा जाता है) को रेंडर करने के लिए, एक ही तरीका इस्तेमाल किया जाता है: टेंप्लेट.
आइए, स्वीकार करें. टेंप्लेट शानदार हैं. बेझिझक पूछें. इसकी परिभाषा से भी आपको गर्म और आरामदायक महसूस होता है:
"…हर बार फिर से बनाने की ज़रूरत नहीं है…" मुझे नहीं पता कि आपको कैसा लगता है, लेकिन मुझे अतिरिक्त काम करने में दिलचस्पी नहीं है. फिर, वेब प्लैटफ़ॉर्म पर, डेवलपर के लिए ज़रूरी सुविधाओं के लिए, नेटिव सपोर्ट क्यों नहीं है?
WhatWG HTML टेंप्लेट की खास बातें, इसका जवाब है. इसमें एक नया <template>
एलिमेंट तय किया गया है, जो क्लाइंट-साइड टेंप्लेट के लिए, स्टैंडर्ड DOM-आधारित तरीके के बारे में बताता है. टेंप्लेट की मदद से, मार्कअप के ऐसे फ़्रैगमेंट तय किए जा सकते हैं जिन्हें एचटीएमएल के तौर पर पार्स किया जाता है. ये फ़्रैगमेंट, पेज लोड होने पर इस्तेमाल नहीं किए जाते, लेकिन रनटाइम के दौरान इन्हें इंस्टैंशिएट किया जा सकता है. राफ़ेल वाइनस्टीन के मुताबिक:
इनमें एचटीएमएल का बड़ा हिस्सा डाला जा सकता है, जिसमें ब्राउज़र को किसी भी वजह से बदलाव करने की अनुमति नहीं है.
राफ़ेल वाइनस्टीन (स्पेशल एपिसोड के लेखक)
फ़ीचर का पता लगाना
<template>
की सुविधा का पता लगाने के लिए, डीओएम एलिमेंट बनाएं और देखें कि .content
प्रॉपर्टी मौजूद है या नहीं:
function supportsTemplate() {
return 'content' in document.createElement('template');
}
if (supportsTemplate()) {
// Good to go!
} else {
// Use old templating techniques or libraries.
}
टेंप्लेट कॉन्टेंट का एलान करना
एचटीएमएल <template>
एलिमेंट, आपके मार्कअप में टेंप्लेट दिखाता है. इसमें "टेंप्लेट कॉन्टेंट" शामिल होते हैं. ये असल में क्लोन किए जा सकने वाले डीओएम के इनऐक्टिव हिस्से होते हैं.
टेंप्लेट को ऐसे स्caf़olding के तौर पर देखें जिनका इस्तेमाल, अपने ऐप्लिकेशन के पूरे जीवनकाल में किया जा सकता है और फिर से इस्तेमाल किया जा सकता है.
टेंप्लेट वाला कॉन्टेंट बनाने के लिए, कुछ मार्कअप तय करें और उसे <template>
एलिमेंट में रैप करें:
<template id="mytemplate">
<img src="" alt="great image">
<div class="comment"></div>
</template>
मुख्य बातें
कॉन्टेंट को <template>
में रैप करने से, हमें कुछ ज़रूरी प्रॉपर्टी मिलती हैं.
ऐप्लिकेशन के चालू होने तक, उसका कॉन्टेंट काम नहीं करता. असल में, आपका मार्कअप छिपा हुआ डीओएम है और वह रेंडर नहीं होता.
टेंप्लेट में मौजूद किसी भी कॉन्टेंट पर इसका कोई असर नहीं पड़ेगा. टेंप्लेट का इस्तेमाल होने तक, स्क्रिप्ट नहीं चलती, इमेज लोड नहीं होतीं, और ऑडियो नहीं चलता.
कॉन्टेंट को दस्तावेज़ में शामिल नहीं माना जाता. मुख्य पेज में
document.getElementById()
याquerySelector()
का इस्तेमाल करने पर, टेंप्लेट के चाइल्ड नोड नहीं दिखेंगे.टेंप्लेट को
<head>
,<body>
या<frameset>
में कहीं भी रखा जा सकता है. साथ ही, उनमें ऐसा कोई भी कॉन्टेंट शामिल किया जा सकता है जिसकी अनुमति उन एलिमेंट में है. ध्यान दें कि "हर जगह" का मतलब है कि<template>
का इस्तेमाल उन जगहों पर सुरक्षित रूप से किया जा सकता है जहां एचटीएमएल पार्स करने वाले टूल की अनुमति नहीं है. हालांकि, कॉन्टेंट मॉडल के बच्चों के लिए, इसका इस्तेमाल किया जा सकता है. इसे<table>
या<select>
के चाइल्ड के तौर पर भी रखा जा सकता है:
<table>
<tr>
<template id="cells-to-repeat">
<td>some content</td>
</template>
</tr>
</table>
टेंप्लेट चालू करना
टेंप्लेट का इस्तेमाल करने के लिए, आपको उसे चालू करना होगा. ऐसा न करने पर, उसका कॉन्टेंट कभी रेंडर नहीं होगा.
ऐसा करने का सबसे आसान तरीका है, document.importNode()
का इस्तेमाल करके, उसके .content
की डीप कॉपी बनाना. .content
प्रॉपर्टी, रीड-ओनली DocumentFragment
होती है. इसमें टेंप्लेट की मुख्य जानकारी होती है.
var t = document.querySelector('#mytemplate');
// Populate the src at runtime.
t.content.querySelector('img').src = 'logo.png';
var clone = document.importNode(t.content, true);
document.body.appendChild(clone);
टेंप्लेट को स्टैंप करने के बाद, उसका कॉन्टेंट "लाइव हो जाता है". इस उदाहरण में, कॉन्टेंट को क्लोन किया गया है, इमेज का अनुरोध किया गया है, और फ़ाइनल मार्कअप रेंडर किया गया है.
डेमो
उदाहरण: इनर्ट स्क्रिप्ट
इस उदाहरण में, टेंप्लेट कॉन्टेंट के काम न करने की जानकारी दी गई है. <script>
सिर्फ़ तब काम करता है, जब बटन दबाया जाता है और टेंप्लेट को स्टैंप किया जाता है.
<button onclick="useIt()">Use me</button>
<div id="container"></div>
<script>
function useIt() {
var content = document.querySelector('template').content;
// Update something in the template DOM.
var span = content.querySelector('span');
span.textContent = parseInt(span.textContent) + 1;
document.querySelector('#container').appendChild(
document.importNode(content, true)
);
}
</script>
<template>
<div>Template used: <span>0</span></div>
<script>alert('Thanks!')</script>
</template>
उदाहरण: टेंप्लेट से शैडो DOM बनाना
ज़्यादातर लोग, मार्कअप की स्ट्रिंग को .innerHTML
पर सेट करके, शैडो DOM को होस्ट से अटैच करते हैं:
<div id="host"></div>
<script>
var shadow = document.querySelector('#host').createShadowRoot();
shadow.innerHTML = '<span>Host node</span>';
</script>
इस तरीके की समस्या यह है कि आपका शैडो डीओएम जितना जटिल होगा, उतनी ही स्ट्रिंग को आपस में जोड़ना पड़ेगा. यह स्केल नहीं होता, चीज़ें तेज़ी से गड़बड़ हो जाती हैं, और बच्चे रोने लगते हैं. इस तरीके से ही, सबसे पहले XSS का जन्म हुआ था! <template>
की मदद ली जा सकती है.
शैडो रूट में टेंप्लेट कॉन्टेंट जोड़कर, सीधे तौर पर DOM के साथ काम करना बेहतर होगा:
<template>
<style>
:host {
background: #f8f8f8;
padding: 10px;
transition: all 400ms ease-in-out;
box-sizing: border-box;
border-radius: 5px;
width: 450px;
max-width: 100%;
}
:host(:hover) {
background: #ccc;
}
div {
position: relative;
}
header {
padding: 5px;
border-bottom: 1px solid #aaa;
}
h3 {
margin: 0 !important;
}
textarea {
font-family: inherit;
width: 100%;
height: 100px;
box-sizing: border-box;
border: 1px solid #aaa;
}
footer {
position: absolute;
bottom: 10px;
right: 5px;
}
</style>
<div>
<header>
<h3>Add a Comment
</header>
<content select="p"></content>
<textarea></textarea>
<footer>
<button>Post</button>
</footer>
</div>
</template>
<div id="host">
<p>Instructions go here</p>
</div>
<script>
var shadow = document.querySelector('#host').createShadowRoot();
shadow.appendChild(document.querySelector('template').content);
</script>
ध्यान देने वाली बातें
<template>
का इस्तेमाल करते समय, मुझे कुछ समस्याएं मिलीं. इनके बारे में यहां बताया गया है:
- अगर modpagespeed का इस्तेमाल किया जा रहा है, तो इस बग से सावधान रहें. इनलाइन
<style scoped>
तय करने वाले टेंप्लेट को, PageSpeed के सीएसएस को फिर से लिखने के नियमों की मदद से हेडर में ले जाया जा सकता है. - किसी टेंप्लेट को "पहले से रेंडर" करने का कोई तरीका नहीं है. इसका मतलब है कि एसेट को पहले से लोड नहीं किया जा सकता, जेएस को प्रोसेस नहीं किया जा सकता, शुरुआती सीएसएस डाउनलोड नहीं की जा सकती वगैरह. यह बात सर्वर और क्लाइंट, दोनों के लिए लागू होती है. टेंप्लेट सिर्फ़ तब रेंडर होता है, जब वह लाइव होता है.
नेस्ट किए गए टेंप्लेट का इस्तेमाल करते समय सावधानी बरतें. ये आपकी उम्मीद के मुताबिक काम नहीं करते. उदाहरण के लिए:
<template> <ul> <template> <li>Stuff</li> </template> </ul> </template>
बाहरी टेंप्लेट को चालू करने से, अंदरूनी टेंप्लेट चालू नहीं होंगे. इसका मतलब है कि नेस्ट किए गए टेंप्लेट के लिए, उनके चाइल्ड टेंप्लेट को भी मैन्युअल तरीके से चालू करना ज़रूरी है.
स्टैंडर्ड बनाने का तरीका
हमें यह नहीं भूलना चाहिए कि हम कहां से आए हैं. स्टैंडर्ड पर आधारित एचटीएमएल टेंप्लेट बनाने में काफ़ी समय लगा. हमने पिछले कुछ सालों में, फिर से इस्तेमाल किए जा सकने वाले टेंप्लेट बनाने के लिए, कुछ बेहतरीन तरकीबें खोजी हैं. नीचे दो सामान्य समस्याएं दी गई हैं जिनका मुझे सामना करना पड़ा है. हम तुलना के लिए, उन्हें इस लेख में शामिल कर रहे हैं.
पहला तरीका: ऑफ़स्क्रीन DOM
लोग "ऑफ़स्क्रीन" DOM बनाने और hidden
एट्रिब्यूट या display:none
का इस्तेमाल करके उसे छिपाने का तरीका, लंबे समय से इस्तेमाल कर रहे हैं.
<div id="mytemplate" hidden>
<img src="logo.png">
<div class="comment"></div>
</div>
यह तकनीक काम करती है, लेकिन इसमें कई समस्याएं हैं. इस तकनीक के बारे में खास जानकारी:
- डीओएम का इस्तेमाल करना - ब्राउज़र को डीओएम के बारे में पता होता है. यह इस काम में अच्छा है. हम इसे आसानी से क्लोन कर सकते हैं.
- कुछ भी रेंडर नहीं किया जाता -
hidden
जोड़ने से ब्लॉक नहीं दिखता. - इस्तेमाल में है - भले ही हमारा कॉन्टेंट छिपा हो, फिर भी इमेज के लिए नेटवर्क का अनुरोध किया जाता है.
- स्टाइल और थीम तय करना मुश्किल है - टेंप्लेट में स्टाइल लागू करने के लिए, एम्बेड किए गए पेज को अपने सभी सीएसएस नियमों के आगे
#mytemplate
लगाना होगा. यह तरीका, नामों के मेल खाने की समस्या को हल करने के लिए सही नहीं है. साथ ही, इस बात की कोई गारंटी नहीं है कि आने वाले समय में, नामों के मेल खाने की समस्या नहीं होगी. उदाहरण के लिए, अगर एम्बेड किए गए पेज में पहले से ही उस आईडी वाला एलिमेंट मौजूद है, तो हम समस्या का हल नहीं कर पाएंगे.
दूसरा तरीका: स्क्रिप्ट को ओवरलोड करना
<script>
को ओवरलोड करने और स्ट्रिंग के तौर पर उसके कॉन्टेंट में बदलाव करने की एक और तकनीक है. ऐसा हो सकता है कि जॉन रेसिग ने साल 2008 में, अपनी माइक्रो टेंप्लेट बनाने की सुविधा के साथ, सबसे पहले इस बारे में बताया हो.
अब कई अन्य टूल भी उपलब्ध हैं. इनमें handlebars.js जैसे कुछ नए टूल भी शामिल हैं.
उदाहरण के लिए:
<script id="mytemplate" type="text/x-handlebars-template">
<img src="logo.png">
<div class="comment"></div>
</script>
इस तकनीक के बारे में खास जानकारी:
- कुछ भी रेंडर नहीं किया जाता - ब्राउज़र इस ब्लॉक को रेंडर नहीं करता, क्योंकि
<script>
डिफ़ॉल्ट रूप सेdisplay:none
होता है. - Inert - ब्राउज़र, स्क्रिप्ट कॉन्टेंट को JS के तौर पर पार्स नहीं करता, क्योंकि उसका टाइप "text/javascript" के अलावा किसी और चीज़ पर सेट होता है.
- सुरक्षा से जुड़ी समस्याएं -
.innerHTML
के इस्तेमाल को बढ़ावा देता है. उपयोगकर्ता से मिले डेटा की रन-टाइम स्ट्रिंग पार्सिंग से, एक्सएसएस (क्रॉस-साइट स्क्रिप्ट) की कमजोरियां आसानी से पैदा हो सकती हैं.
नतीजा
क्या आपको याद है कि jQuery ने डीओएम के साथ काम करना कितना आसान बना दिया था? इस वजह से, querySelector()
/querySelectorAll()
को प्लैटफ़ॉर्म पर जोड़ा गया. यह तो साफ़ तौर पर जीत है, है ना? सीएसएस सिलेक्टर और स्टैंडर्ड की मदद से, डीओएम को फ़ेच करने की सुविधा को लोकप्रिय बनाने वाली लाइब्रेरी ने बाद में इसे अपना लिया. ऐसा हमेशा नहीं होता, लेकिन जब ऐसा होता है, तो मुझे बहुत अच्छा लगता है.
मुझे लगता है कि <template>
भी इसी तरह का मामला है. इससे क्लाइंट-साइड टेंप्लेट बनाने के हमारे तरीके को स्टैंडर्ड किया जाता है. हालांकि, इससे ज़्यादा अहम बात यह है कि इससे साल 2008 के हमारे हैक की ज़रूरत नहीं पड़ती.
मेरी राय में, वेब लेखन की पूरी प्रोसेस को बेहतर, आसानी से मैनेज किया जा सकने वाला, और ज़्यादा सुविधाओं वाला बनाना हमेशा अच्छा होता है.
अन्य संसाधन
- WhatWG स्पेसिफ़िकेशन
- वेब कॉम्पोनेंट के बारे में जानकारी
- <web>components</web> (वीडियो) - यह आपके लिए एक बेहतरीन और पूरी जानकारी देने वाला प्रज़ेंटेशन है.