ক্লায়েন্ট-সাইড টেমপ্লেটিং স্ট্যান্ডার্ডাইজ করা
ভূমিকা
ওয়েব ডেভেলপমেন্টের ক্ষেত্রে টেমপ্লেট তৈরি করার ধারণা নতুন নয়। আসলে, Django (Python), ERB/Haml (Ruby) এবং Smarty (PHP)-এর মতো সার্ভার-সাইড টেমপ্লেটিং ভাষা/ইঞ্জিন অনেক দিন ধরেই ব্যবহার করা হচ্ছে। তবে গত কয়েক বছরে, আমরা MVC ফ্রেমওয়ার্কের ব্যাপক ব্যবহার দেখেছি। এগুলির সবকটিই একে অপরের থেকে সামান্য আলাদা, তবুও বেশিরভাগই তাদের প্রেজেন্টেশনাল লেয়ার (ওরফে ভিউ) রেন্ডার করার জন্য একটি সাধারণ মেকানিক শেয়ার করে: টেমপ্লেট।
আসুন, এর মুখোমুখি হওয়া যাক। টেমপ্লেটগুলি দুর্দান্ত। নির্দ্বিধায় জিজ্ঞাসা করুন। এমনকি এর সংজ্ঞা আপনাকে উষ্ণ ও আরামদায়ক অনুভূতি দেয়:
"…প্রতিবার নতুন করে তৈরি করতে হয় না…" আপনার কথা জানি না, তবে আমি অতিরিক্ত কাজ এড়িয়ে যেতে পছন্দ করি। তাহলে কেন ওয়েব প্ল্যাটফর্মে এমন কিছুর জন্য নেটিভ সাপোর্ট নেই যা ডেভেলপারদের কাছে স্পষ্টতই গুরুত্বপূর্ণ?
WhatWG HTML টেমপ্লেট স্পেসিফিকেশন হল উত্তর। এটি একটি নতুন <template> এলিমেন্টকে সংজ্ঞায়িত করে যা ক্লায়েন্ট-সাইড টেমপ্লেটিংয়ের জন্য একটি স্ট্যান্ডার্ড DOM-ভিত্তিক পদ্ধতির বর্ণনা করে। টেমপ্লেট আপনাকে মার্কআপের ফ্র্যাগমেন্ট ঘোষণা করার অনুমতি দেয় যা HTML হিসেবে পার্স করা হয়, পৃষ্ঠা লোড হওয়ার সময় অব্যবহৃত থাকে, কিন্তু রানটাইমে পরে ইনস্ট্যান্টিয়েট করা যেতে পারে। Rafael Weinstein-এর কথা উদ্ধৃত করে বলা যায়:
এগুলি হল এমন একটি জায়গা যেখানে HTML-এর একটি বড় অংশ রাখা যায় যা আপনি চান না যে ব্রাউজার কোনও কারণে…একেবারেই পরিবর্তন করুক।
Rafael Weinstein (spec author)
ফিচার শনাক্তকরণ
<template> ফিচার শনাক্ত করতে, DOM এলিমেন্ট তৈরি করুন এবং .content প্রপার্টি আছে কিনা তা চেক করুন:
function supportsTemplate() {
return 'content' in document.createElement('template');
}
if (supportsTemplate()) {
// Good to go!
} else {
// Use old templating techniques or libraries.
}
টেমপ্লেট কন্টেন্ট ঘোষণা করা
HTML <template> এলিমেন্ট আপনার মার্কআপে একটি টেমপ্লেটকে দেখায়। এতে
"টেমপ্লেট কন্টেন্ট" আছে; মূলত ক্লোন করা যায় এমন DOM-এর ইনার্ট চাঙ্ক।
টেমপ্লেটকে স্ক্যাফোল্ডিংয়ের অংশ হিসেবে ভাবুন যা আপনি অ্যাপের লাইফটাইম জুড়ে ব্যবহার (এবং আবার ব্যবহার) করতে পারবেন।
টেমপ্লেট করা কন্টেন্ট তৈরি করতে, কিছু মার্কআপ ঘোষণা করুন এবং এটিকে <template> এলিমেন্টে র্যাপ করুন:
<template id="mytemplate">
<img src="" alt="great image">
<div class="comment"></div>
</template>
থাম
<template>-এ কন্টেন্ট র্যাপ করলে আমরা কিছু গুরুত্বপূর্ণ প্রপার্টি পাই।
অ্যাক্টিভেট না করা পর্যন্ত এর কন্টেন্ট কার্যকরভাবে নিষ্ক্রিয় থাকে। মূলত, আপনার মার্কআপ হল লুকানো DOM এবং এটি রেন্ডার করে না।
টেমপ্লেটের মধ্যে থাকা কোনও কন্টেন্টের সাইড এফেক্ট থাকবে না। স্ক্রিপ্ট রান করে না, ছবি লোড হয় না, অডিও চলে না,…যতক্ষণ না টেমপ্লেট ব্যবহার করা হয়।
কন্টেন্ট ডকুমেন্টে নেই বলে বিবেচনা করা হয়। মূল পৃষ্ঠায়
document.getElementById()বাquerySelector()ব্যবহার করলে টেমপ্লেটের চাইল্ড নোড ফেরত পাওয়া যাবে না।টেমপ্লেট
<head>,<body>বা<frameset>-এর মধ্যে যেকোনও জায়গায় রাখা যেতে পারে এবং এতে যেকোনও ধরনের কন্টেন্ট থাকতে পারে যা এইসব এলিমেন্টে অনুমোদিত। মনে রাখবেন, "যেকোনও জায়গায়" মানে হল,<template>নিরাপদে এমন জায়গায় ব্যবহার করা যেতে পারে যেখানে HTML পার্সার অনুমতি দেয় না…সব কিন্তু কন্টেন্ট মডেল চিলড্রেন। এটি<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-এ মার্কআপের একটি স্ট্রিং সেট করে হোস্টের সাথে Shadow DOM অ্যাটাচ করেন:
<div id="host"></div>
<script>
var shadow = document.querySelector('#host').createShadowRoot();
shadow.innerHTML = '<span>Host node</span>';
</script>
এই পদ্ধতির সমস্যা হল, আপনার শ্যাডো DOM যত জটিল হবে,
আপনাকে তত বেশি স্ট্রিং কনক্যাটেনেশন করতে হবে। এটি স্কেল করে না, জিনিসগুলি দ্রুত অগোছালো হয়ে যায় এবং বাচ্চারা কাঁদতে শুরু করে। এই পদ্ধতিতেই 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>
Gotchas
<template> ব্যবহার করার সময় আমি যেসব সমস্যার সম্মুখীন হয়েছি, তার কয়েকটি এখানে উল্লেখ করা হল:
- আপনি modpagespeed ব্যবহার করলে,
এই সমস্যা সম্পর্কে সতর্ক থাকুন। ইনলাইন
<style scoped>সংজ্ঞায়িত করে এমন টেমপ্লেট, PageSpeed-এর CSS রিরাইটিং নিয়মের মাধ্যমে অনেকগুলি হেডে সরানো হতে পারে। - কোনও টেমপ্লেট "প্রিরেন্ডার" করার কোনও উপায় নেই, অর্থাৎ আপনি অ্যাসেট প্রিলোড করতে, JS প্রসেস করতে, প্রাথমিক CSS ডাউনলোড করতে পারবেন না। এটি সার্ভার ও ক্লায়েন্ট, উভয়ের ক্ষেত্রেই প্রযোজ্য। টেমপ্লেট লাইভ হওয়ার সময় শুধুমাত্র একবারই রেন্ডার হয়।
নেস্টেড টেমপ্লেট ব্যবহার করার সময় সতর্ক থাকুন। আপনি যেমনটি আশা করেন, তারা তেমন আচরণ করে না। যেমন:
<template> <ul> <template> <li>Stuff</li> </template> </ul> </template>বাইরের টেমপ্লেট অ্যাক্টিভেট করলে ভিতরের টেমপ্লেট অ্যাক্টিভেট হবে না। অর্থাৎ, নেস্ট করা টেমপ্লেটের ক্ষেত্রে চাইল্ড টেমপ্লেটও ম্যানুয়ালি অ্যাক্টিভেট করতে হয়।
স্ট্যান্ডার্ডে পৌঁছানোর পথ
আমরা কোথা থেকে এসেছি তা ভুলে গেলে চলবে না। স্ট্যান্ডার্ড-ভিত্তিক HTML টেমপ্লেট তৈরি করার পথটি দীর্ঘ। বহু বছর ধরে, আমরা আবার ব্যবহার করা যায় এমন টেমপ্লেট তৈরি করার জন্য কিছু চতুর কৌশল নিয়ে এসেছি। নিচে এমন দুটি সাধারণ সমস্যার কথা উল্লেখ করা হল যেগুলি আমি প্রায়ই দেখতে পাই। তুলনা করার জন্য আমি এই নিবন্ধে সেগুলি অন্তর্ভুক্ত করছি।
পদ্ধতি ১: অফস্ক্রিন DOM
অনেকদিন ধরে লোকজন যে পদ্ধতি ব্যবহার করে আসছেন তা হল "অফস্ক্রিন"
DOM তৈরি করা এবং hidden অ্যাট্রিবিউট বা display:none ব্যবহার করে ভিউ থেকে এটি লুকানো।
<div id="mytemplate" hidden>
<img src="logo.png">
<div class="comment"></div>
</div>
এই টেকনিক কাজ করলেও, এর কিছু খারাপ দিক আছে। এই টেকনিকের সংক্ষিপ্ত বিবরণ:
- DOM ব্যবহার করা - ব্রাউজার DOM জানে। এটি এই কাজে দক্ষ। আমরা সহজেই এটি ক্লোন করতে পারি।
- কিছুই রেন্ডার করা হয়নি -
hiddenযোগ করলে ব্লক দেখানো যাবে না। - ইনার্ট নয় - আমাদের কন্টেন্ট লুকানো থাকলেও, ছবির জন্য এখনও নেটওয়ার্ক অনুরোধ করা হয়।
- স্টাইলিং ও থিমিংয়ের ক্ষেত্রে সমস্যা - টেমপ্লেটে স্টাইল প্রয়োগ করার জন্য, এম্বেড করা পৃষ্ঠার সব CSS নিয়মের আগে
#mytemplateযোগ করতে হবে। এটি ভঙ্গুর এবং ভবিষ্যতে নামের সংঘর্ষের সম্মুখীন হব না এমন কোনও গ্যারেন্টি নেই। যেমন, এম্বেডিং পৃষ্ঠায় আগে থেকেই সেই আইডি সহ কোনও এলিমেন্ট থাকলে, আমরা সমস্যায় পড়ব।
পদ্ধতি ২: স্ক্রিপ্ট ওভারলোড করা
অন্য একটি কৌশল হল <script> ওভারলোড করা এবং স্ট্রিং হিসেবে এর কন্টেন্ট
ম্যানিপুলেট করা। জন রেসিগ সম্ভবত ২০০৮ সালে তার মাইক্রো টেমপ্লেটিং ইউটিলিটি-এর মাধ্যমে প্রথম এটি দেখান।
এখন আরও অনেক আছে, যার মধ্যে 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 যখন DOM-এর সাথে কাজ করাকে অত্যন্ত সহজ করে তুলেছিল? ফলাফল হল querySelector()/querySelectorAll()
প্ল্যাটফর্মে যোগ করা হচ্ছে। স্পষ্টতই জয়, তাই না? CSS বাছাইকারীর সাহায্যে DOM ফেচ করার পদ্ধতিকে
একটি লাইব্রেরি জনপ্রিয় করে তোলে এবং পরে স্ট্যান্ডার্ডগুলি এটি গ্রহণ করে। সব সময় এমনটা হয় না, তবে যখন হয় তখন আমার দারুণ লাগে।
আমার মনে হয় <template>-এ একই ধরনের ঘটনা ঘটেছে। এটি ক্লায়েন্ট-সাইড টেমপ্লেটিংয়ের পদ্ধতিকে স্ট্যান্ডার্ডাইজ করে, তবে আরও গুরুত্বপূর্ণ হল, এটি ২০০৮ হ্যাকের প্রয়োজনীয়তা দূর করে।
আমার মতে, সম্পূর্ণ ওয়েব অথরিং প্রসেসকে আরও সহজ, আরও রক্ষণাবেক্ষণযোগ্য এবং আরও
ফিচার-সমৃদ্ধ করা সবসময়ই একটি ভালো জিনিস।
অতিরিক্ত রিসোর্স
- WhatWG স্পেসিফিকেশন
- ওয়েব কম্পোনেন্টের ভূমিকা
- <web>কম্পোনেন্ট</web> (ভিডিও) - আপনার বিশ্বস্ত বন্ধুর দ্বারা একটি চমৎকার ব্যাপক উপস্থাপনা।