HTML' का नया टेंप्लेट टैग

क्लाइंट-साइड टेंप्लेट के लिए स्टैंडर्ड तय करना

परिचय

वेब डेवलपमेंट के लिए टेंप्लेट का कॉन्सेप्ट नया नहीं है. असल में, Django (Python), ERB/Haml (Ruby), और Smarty (PHP) जैसी सर्वर-साइड टेंप्लेट बनाने वाली भाषाएं/इंजन, लंबे समय से मौजूद हैं. हालांकि, पिछले कुछ सालों में, हमें एमवीसी फ़्रेमवर्क का ज़बरदस्त इस्तेमाल हुआ है. ये सभी टूल थोड़े अलग होते हैं. हालांकि, ज़्यादातर टूल में प्रज़ेंटेशन लेयर (जिसे डेटा व्यू भी कहा जाता है) को रेंडर करने के लिए एक ही तरीका इस्तेमाल किया जाता है: टेंप्लेट.

यह मानकर चलते हैं. टेंप्लेट शानदार होते हैं. बेझिझक पूछें. इसकी परिभाषा से भी आपको गर्म और आरामदायक महसूस होता है:

"…हर बार फिर से बनाने की ज़रूरत नहीं है…" मुझे नहीं पता कि आपको कैसा लगता है, लेकिन मुझे अतिरिक्त काम करने में दिलचस्पी नहीं है. फिर, वेब प्लैटफ़ॉर्म पर, डेवलपर के लिए ज़रूरी सुविधाओं के लिए, नेटिव सपोर्ट क्यों नहीं है?

WhatWG HTML टेंप्लेट की खास बातें से आपको इसका जवाब मिल जाएगा. यह एक नए <template> एलिमेंट के बारे में बताता है, जो क्लाइंट-साइड टेंप्लेट के लिए स्टैंडर्ड डीओएम पर आधारित तरीके के बारे में बताता है. टेंप्लेट की मदद से, मार्कअप के ऐसे फ़्रैगमेंट तय किए जा सकते हैं जिन्हें एचटीएमएल के तौर पर पार्स किया जाता है. ये फ़्रैगमेंट, पेज लोड होने पर इस्तेमाल नहीं किए जाते, लेकिन रनटाइम के दौरान इन्हें इंस्टैंशिएट किया जा सकता है. राफ़ेल वाइंस्टीन को कोट करने के लिए:

यहां पर HTML का बहुत बड़ा संग्रह होता है और आप नहीं चाहते कि ब्राउज़र किसी भी स्थिति में...किसी भी कारण से गलत हो.

राफ़ेल वाइनस्टीन (स्पेसिफ़िकेशन के लेखक)

फ़ीचर का पता लगाना

<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> में रैप करने से, हमें कुछ ज़रूरी प्रॉपर्टी मिलती हैं.

  1. ऐप्लिकेशन के चालू होने तक, उसका कॉन्टेंट काम नहीं करता. असल में, आपका मार्कअप छिपा हुआ DOM है और रेंडर नहीं होता है.

  2. टेंप्लेट में मौजूद किसी भी कॉन्टेंट का खराब असर नहीं होगा. टेंप्लेट का इस्तेमाल होने तक, स्क्रिप्ट नहीं चलती, इमेज लोड नहीं होतीं, और ऑडियो नहीं चलता.

  3. कॉन्टेंट को दस्तावेज़ में शामिल नहीं माना जाता. मुख्य पेज में document.getElementById() या querySelector() का इस्तेमाल करने से, टेंप्लेट के चाइल्ड नोड नहीं दिखेंगे.

  4. टेंप्लेट को <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 होता है.
  • इनर्ट - ब्राउज़र, स्क्रिप्ट कॉन्टेंट को JS के तौर पर पार्स नहीं करता, क्योंकि इसका टाइप "text/javascript" के अलावा किसी और चीज़ पर सेट होता है.
  • सुरक्षा से जुड़ी समस्याएं - .innerHTML के इस्तेमाल को बढ़ावा देता है. उपयोगकर्ता से मिले डेटा की रन-टाइम स्ट्रिंग पार्सिंग से, एक्सएसएस (क्रॉस-साइट स्क्रिप्ट) की कमजोरियां आसानी से पैदा हो सकती हैं.

नतीजा

क्या आपको याद है कि jQuery ने डीओएम के साथ काम करना कितना आसान बना दिया था? इस वजह से, querySelector()/querySelectorAll() को प्लैटफ़ॉर्म पर जोड़ा गया. यह तो साफ़ तौर पर जीत है, है ना? बाद में, एक लाइब्रेरी ने सीएसएस सिलेक्टर और स्टैंडर्ड की मदद से, DOM को फ़ेच करने के लिए इसे लोकप्रिय बनाया. ऐसा हमेशा नहीं होता, लेकिन जब ऐसा होता है, तो मुझे बहुत अच्छा लगता है.

मुझे लगता है कि <template> भी इसी तरह का मामला है. यह क्लाइंट-साइड टेंप्लेट बनाने के हमारे तरीके को मानक बनाता है, लेकिन सबसे अहम बात यह है कि इससे 2008 के हमारे हैकिंग की ज़रूरत नहीं पड़ती. मेरी राय में, वेब लेखन की पूरी प्रोसेस को बेहतर, आसानी से मैनेज किया जा सकने वाला, और ज़्यादा सुविधाओं वाला बनाना हमेशा अच्छा होता है.

अन्य संसाधन