एचटीएमएल का नया टेंप्लेट टैग

क्लाइंट-साइड टेंप्लेट को स्टैंडर्ड बनाना

परिचय

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

आइए, इस बात को स्वीकार करें. टेंप्लेट शानदार हैं. बिंदास पूछें. इसकी परिभाषा से भी आपको सुकून और आराम का एहसास होता है:

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

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

ये एचटीएमएल के बड़े बंडल को रखने की जगह हैं. इनमें मौजूद एचटीएमएल में ब्राउज़र किसी भी वजह से कोई बदलाव नहीं कर सकता.

राफ़ेल वाइनस्टीन (spec author)

सुविधा का पता लगाने की सुविधा

<template> की सुविधा का पता लगाने के लिए, डीओएम एलिमेंट बनाएं और देखें कि .content प्रॉपर्टी मौजूद है या नहीं:

function supportsTemplate() {
    return 'content' in document.createElement('template');
}

if (supportsTemplate()) {
    // Good to go!
} else {
    // Use old templating techniques or libraries.
}

टेंप्लेट कॉन्टेंट तय करना

एचटीएमएल <template> एलिमेंट, आपके मार्कअप में मौजूद टेंप्लेट को दिखाता है. इसमें "टेंप्लेट कॉन्टेंट" होता है. यह असल में, क्लोन किए जा सकने वाले डीओएम के इनर्ट चंक होते हैं. टेंप्लेट को ऐसे फ़्रेमवर्क के तौर पर देखें जिन्हें अपने ऐप्लिकेशन के पूरे लाइफ़साइकल के दौरान इस्तेमाल (और फिर से इस्तेमाल) किया जा सकता है.

टेंप्लेट वाला कॉन्टेंट बनाने के लिए, कुछ मार्कअप तय करें और उसे <template> एलिमेंट में रैप करें:

<template id="mytemplate">
    <img src="" alt="great image">
    <div class="comment"></div>
</template>

स्तंभ

कॉन्टेंट को <template> में रैप करने से, हमें कुछ अहम प्रॉपर्टी मिलती हैं.

  1. इसका कॉन्टेंट तब तक काम नहीं करता, जब तक उसे चालू न किया जाए. इसका मतलब है कि आपका मार्कअप, छिपा हुआ डीओएम है और रेंडर नहीं होता.

  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 पर मार्कअप की स्ट्रिंग सेट करके, शैडो डीओएम को होस्ट से अटैच करते हैं:

<div id="host"></div>
<script>
  var shadow = document.querySelector('#host').createShadowRoot();
  shadow.innerHTML = '<span>Host node</span>';
</script>

इस तरीके में समस्या यह है कि आपका शैडो डीओएम जितना ज़्यादा जटिल होता जाएगा, उतनी ही ज़्यादा स्ट्रिंग जोड़ने की प्रोसेस आपको करनी होगी. यह तरीका बड़े पैमाने पर काम नहीं करता, चीज़ें जल्दी ही गड़बड़ हो जाती हैं, और बच्चे रोने लगते हैं. इसी वजह से, XSS की शुरुआत हुई! <template> मदद के लिए तैयार है.

टेंप्लेट के कॉन्टेंट को शैडो रूट में जोड़कर, सीधे डीओएम के साथ काम करना ज़्यादा सही रहेगा:

<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>

Gotchas

<template> का इस्तेमाल करते समय, मुझे कुछ ऐसी चीज़ें मिली हैं जो आपको भी पता होनी चाहिए:

  • अगर modpagespeed का इस्तेमाल किया जा रहा है, तो इस गड़बड़ी से सावधान रहें. इनलाइन <style scoped> को तय करने वाले टेंप्लेट, PageSpeed के सीएसएस को फिर से लिखने के नियमों की मदद से, हेड पर ले जाए जा सकते हैं.
  • किसी टेंप्लेट को "प्रीरेंडर" करने का कोई तरीका नहीं है. इसका मतलब है कि ऐसेट को पहले से लोड नहीं किया जा सकता, जेएस को प्रोसेस नहीं किया जा सकता, शुरुआती सीएसएस को डाउनलोड नहीं किया जा सकता वगैरह. यह बात सर्वर और क्लाइंट, दोनों पर लागू होती है. टेंप्लेट सिर्फ़ तब रेंडर होता है, जब वह लाइव होता है.
  • नेस्ट किए गए टेंप्लेट का इस्तेमाल करते समय सावधानी बरतें. वे आपकी उम्मीद के मुताबिक काम नहीं करते. उदाहरण के लिए:

    <template>
      <ul>
        <template>
          <li>Stuff</li>
        </template>
      </ul>
    </template>
    

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

स्टैंडर्ड बनाने का सफ़र

हमें यह नहीं भूलना चाहिए कि हम कहां से आए हैं. मानकों पर आधारित एचटीएमएल टेंप्लेट का सफ़र काफ़ी लंबा रहा है. पिछले कुछ सालों में, हमने फिर से इस्तेमाल किए जा सकने वाले टेंप्लेट बनाने के लिए, कुछ बेहतरीन तरीके खोजे हैं. यहां दो सामान्य समस्याएं दी गई हैं, जो मुझे देखने को मिली हैं. हम इस लेख में, तुलना करने के लिए उन्हें शामिल कर रहे हैं.

पहला तरीका: ऑफ़स्क्रीन डीओएम

लोग लंबे समय से एक तरीका इस्तेमाल कर रहे हैं. इसमें "ऑफ़स्क्रीन" 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 का इस्तेमाल करने के लिए बढ़ावा देता है. उपयोगकर्ता से मिले डेटा की रन-टाइम स्ट्रिंग पार्सिंग से, आसानी से XSS जोखिम हो सकते हैं.

नतीजा

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

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

अन्य संसाधन