Shadow DOM ওয়েব ডেভেলপারদের ওয়েব কম্পোনেন্টের জন্য কম্পার্টমেন্টালাইজড DOM ও CSS তৈরি করার সুবিধা দেয়
সারাংশ
Shadow DOM ওয়েব অ্যাপ তৈরির ভঙ্গুরতা দূর করে। HTML, CSS ও JS-এর গ্লোবাল প্রকৃতির কারণে
এই ভঙ্গুরতা আসে। বহু বছর ধরে আমরা
সমস্যা এড়ানোর জন্য
অসংখ্য টুল
তৈরি
করেছি। যেমন, আপনি যখন নতুন HTML আইডি/ক্লাস ব্যবহার করেন,
তখন এটি পৃষ্ঠার ব্যবহার করা কোনও নামের সাথে কনফ্লিক্ট করবে কিনা তা বলা যায় না।
ছোটখাটো বাগ দেখা দেয়,
CSS স্পেসিফিসিটি একটি বিশাল সমস্যা হয়ে দাঁড়ায় (!important সব কিছু!), স্টাইল
সিলেক্টর নিয়ন্ত্রণের বাইরে চলে যায় এবং
পারফর্ম্যান্সের ক্ষতি হতে পারে। তালিকাটি
চলতেই থাকে।
Shadow DOM, CSS ও DOM ঠিক করে। এটি ওয়েব প্ল্যাটফর্মে স্কোপড স্টাইল নিয়ে আসে। টুল বা নামকরণ কনভেনশন ছাড়াই, আপনি মার্কআপের সাথে CSS বান্ডেল করতে, বাস্তবায়নের বিবরণ লুকাতে এবং ভ্যানিলা জাভাস্ক্রিপ্টে স্বয়ংসম্পূর্ণ কম্পোনেন্ট তৈরি করতে পারবেন।
ভূমিকা
Shadow DOM হল তিনটি ওয়েব কম্পোনেন্ট স্ট্যান্ডার্ডের মধ্যে একটি: HTML টেমপ্লেট, Shadow DOM এবং কাস্টম এলিমেন্ট। HTML ইমপোর্ট আগে তালিকার অংশ ছিল কিন্তু এখন বাতিল করা হয়েছে বলে বিবেচনা করা হয়।
আপনাকে শ্যাডো DOM ব্যবহার করে এমন ওয়েব কম্পোনেন্ট তৈরি করতে হবে না। কিন্তু আপনি যখন এটি করেন, তখন আপনি এর সুবিধাগুলি (CSS স্কোপিং, DOM এনক্যাপসুলেশন, কম্পোজিশন) গ্রহণ করেন এবং পুনরায় ব্যবহারযোগ্য কাস্টম এলিমেন্ট তৈরি করেন, যা স্থিতিস্থাপক, অত্যন্ত কনফিগারযোগ্য এবং অত্যন্ত পুনরায় ব্যবহারযোগ্য। কাস্টম এলিমেন্ট যদি নতুন HTML (JS API সহ) তৈরি করার উপায় হয়, তাহলে শ্যাডো DOM হল এর HTML ও CSS প্রদান করার উপায়। দুটি API একত্রিত হয়ে সেল্ফ-কন্টেনড HTML, CSS ও JavaScript-এর মাধ্যমে একটি কম্পোনেন্ট তৈরি করে।
কম্পোনেন্ট-ভিত্তিক অ্যাপ তৈরির টুল হিসেবে Shadow DOM ডিজাইন করা হয়েছে। তাই, এটি ওয়েব ডেভেলপমেন্টের সাধারণ সমস্যার সমাধান নিয়ে আসে:
- আইসোলেটেড DOM: কোনও কম্পোনেন্টের DOM স্বয়ংসম্পূর্ণ (যেমন
document.querySelector()কম্পোনেন্টের শ্যাডো DOM-এ নোড রিটার্ন করবে না)। - স্কোপড CSS: শ্যাডো DOM-এর মধ্যে CSS সংজ্ঞায়িত করা হয়। স্টাইল সংক্রান্ত নিয়ম বাইরে লিক হয় না এবং পৃষ্ঠার স্টাইল ভিতরে ব্লিড করে না।
- কম্পোজিশন: আপনার কম্পোনেন্টের জন্য একটি ডিক্লারেটিভ, মার্কআপ-ভিত্তিক API ডিজাইন করুন।
- CSS সহজ করে তোলে - স্কোপড DOM-এর অর্থ হল আপনি সহজ CSS বাছাইকারী, আরও সাধারণ আইডি/ক্লাসের নাম ব্যবহার করতে পারবেন এবং নামের মধ্যে দ্বন্দ্ব নিয়ে চিন্তা করতে হবে না।
- প্রোডাক্টিভিটি - একটি বড় (গ্লোবাল) পৃষ্ঠার পরিবর্তে DOM-এর চাঙ্ক হিসেবে অ্যাপের কথা ভাবুন।
fancy-tabs ডেমো
এই নিবন্ধে, আমি একটি ডেমো কম্পোনেন্ট (<fancy-tabs>)
এবং এর থেকে কোড স্নিপেট উল্লেখ করব। আপনার ব্রাউজার API-কে সমর্থন করলে, আপনি
ঠিক নিচে এটির একটি লাইভ ডেমো দেখতে পাবেন। অন্যথায়, Github-এ সম্পূর্ণ সোর্স চেক করে দেখুন।
শ্যাডো DOM কী?
DOM-এর ব্যাকগ্রাউন্ড
HTML-এর সাহায্যে ওয়েব চলে কারণ এটি ব্যবহার করা সহজ। কয়েকটি ট্যাগ ঘোষণা করে, আপনি কয়েক সেকেন্ডের মধ্যে এমন একটি পৃষ্ঠা তৈরি করতে পারবেন যাতে প্রেজেন্টেশন ও স্ট্রাকচার, দুটিই আছে। তবে, HTML নিজে থেকে খুব একটা উপযোগী নয়। মানুষের পক্ষে টেক্সট-ভিত্তিক ভাষা বোঝা সহজ, কিন্তু মেশিনের জন্য আরও কিছু প্রয়োজন। ডকুমেন্ট অবজেক্ট মডেল বা DOM লিখুন।
ব্রাউজার কোনও ওয়েব পেজ লোড করার সময় অনেক আকর্ষণীয় কাজ করে। এটি যেসব কাজ করে তার মধ্যে একটি হল, লেখকের HTML-কে লাইভ ডকুমেন্টে পরিবর্তন করা। মূলত, পৃষ্ঠার স্ট্রাকচার বুঝতে, ব্রাউজার HTML (টেক্সটের স্ট্যাটিক স্ট্রিং) একটি ডেটা মডেলে (অবজেক্ট/নোড) পার্স করে। ব্রাউজার এই নোডগুলির একটি ট্রি তৈরি করে HTML-এর হায়ারার্কি সংরক্ষণ করে: DOM। DOM-এর সবচেয়ে ভালো দিক হল এটি আপনার পৃষ্ঠার লাইভ উপস্থাপনা। আমাদের লেখা স্ট্যাটিক HTML-এর মতো নয়, ব্রাউজার-প্রডিউসড নোডে প্রপার্টি, পদ্ধতি এবং সবচেয়ে ভালো হল… প্রোগ্রাম দ্বারা ম্যানিপুলেট করা যায়! তাই আমরা সরাসরি জাভাস্ক্রিপ্ট ব্যবহার করে DOM এলিমেন্ট তৈরি করতে পারি:
const header = document.createElement('header');
const h1 = document.createElement('h1');
h1.textContent = 'Hello DOM';
header.appendChild(h1);
document.body.appendChild(header);
নিম্নলিখিত HTML মার্কআপ তৈরি করে:
<body>
<header>
<h1>Hello DOM</h1>
</header>
</body>
সবকিছুই ঠিক আছে। তাহলে শ্যাডো DOM আসলে কী?
DOM… in the shadows
শ্যাডো DOM হল সাধারণ DOM, তবে দু'টি পার্থক্য আছে: ১) এটি কীভাবে তৈরি/ব্যবহার করা হয় এবং
২) পৃষ্ঠার বাকি অংশের সাথে এটি কীভাবে কাজ করে। সাধারণত, আপনি DOM
নোড তৈরি করেন এবং অন্য এলিমেন্টের চাইল্ড হিসেবে সেগুলি যোগ করেন। Shadow DOM-এর মাধ্যমে, আপনি
একটি স্কোপড DOM ট্রি তৈরি করেন যা এলিমেন্টের সাথে অ্যাটাচ করা থাকে, কিন্তু সেটির
আসল চিলড্রেন থেকে আলাদা হয়। এই স্কোপ করা সাবট্রিকে শ্যাডো ট্রি বলা হয়। যে এলিমেন্টের সাথে এটি অ্যাটাচ করা আছে সেটি হল এর শ্যাডো হোস্ট। শ্যাডোতে আপনি যা কিছু যোগ করেন তা হোস্টিং এলিমেন্টের
লোকাল হয়ে যায়, এর মধ্যে <style> অন্তর্ভুক্ত। এইভাবে শ্যাডো DOM
CSS স্টাইল স্কোপিং অর্জন করে।
শ্যাডো DOM তৈরি করা হচ্ছে
শ্যাডো রুট হল একটি ডকুমেন্ট ফ্র্যাগমেন্ট যা "হোস্ট" এলিমেন্টের সাথে অ্যাটাচ করা থাকে।
শ্যাডো রুট অ্যাটাচ করার মাধ্যমে এলিমেন্টটি তার শ্যাডো DOM পায়। কোনও
এলিমেন্টের জন্য শ্যাডো DOM তৈরি করতে, element.attachShadow() কল করুন:
const header = document.createElement('header');
const shadowRoot = header.attachShadow({mode: 'open'});
shadowRoot.innerHTML = '<h1>Hello Shadow DOM</h1>'; // Could also use appendChild().
// header.shadowRoot === shadowRoot
// shadowRoot.host === header
আমি শ্যাডো রুট পূরণ করার জন্য .innerHTML ব্যবহার করছি, তবে আপনি অন্যান্য DOM
API-ও ব্যবহার করতে পারেন। এটি হল ওয়েব। আমাদের কাছে বিকল্প আছে।
স্পেক এলিমেন্টের একটি তালিকা নির্ধারণ করে যা শ্যাডো ট্রি হোস্ট করতে পারে না। কোনও এলিমেন্ট তালিকায় থাকার একাধিক কারণ থাকতে পারে:
- ব্রাউজার আগে থেকেই এলিমেন্টের জন্য নিজস্ব ইন্টার্নাল শ্যাডো DOM হোস্ট করে
(
<textarea>,<input>)। - এলিমেন্টের জন্য শ্যাডো DOM (
<img>) হোস্ট করার কোনও মানে হয় না।
যেমন, এটি কাজ করে না:
document.createElement('input').attachShadow({mode: 'open'});
// Error. `<input>` cannot host shadow dom.
কাস্টম এলিমেন্টের জন্য শ্যাডো DOM তৈরি করা হচ্ছে
কাস্টম এলিমেন্ট তৈরি করার সময় শ্যাডো DOM বিশেষভাবে উপযোগী। কোনও এলিমেন্টের HTML, CSS ও JS কম্পার্টমেন্টালাইজ করতে শ্যাডো DOM ব্যবহার করুন, এর ফলে "ওয়েব কম্পোনেন্ট" তৈরি হয়।
উদাহরণ - একটি কাস্টম এলিমেন্ট নিজেই শ্যাডো DOM অ্যাটাচ করে, তার DOM/CSS এনক্যাপসুলেট করে:
// Use custom elements API v1 to register a new HTML tag and define its JS behavior
// using an ES6 class. Every instance of <fancy-tab> will have this same prototype.
customElements.define('fancy-tabs', class extends HTMLElement {
constructor() {
super(); // always call super() first in the constructor.
// Attach a shadow root to <fancy-tabs>.
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `
<style>#tabs { ... }</style> <!-- styles are scoped to fancy-tabs! -->
<div id="tabs">...</div>
<div id="panels">...</div>
`;
}
...
});
এখানে কয়েকটি আকর্ষণীয় জিনিস চলছে। প্রথমটি হল, <fancy-tabs>-এর ইনস্ট্যান্স তৈরি করার সময়
কাস্টম এলিমেন্ট নিজস্ব শ্যাডো DOM তৈরি করে। এটি constructor()-এ করা হয়। দ্বিতীয়ত, আমরা শ্যাডো রুট তৈরি করছি বলে,
<style>-এর মধ্যে CSS নিয়ম <fancy-tabs>-এর স্কোপে থাকবে।
কম্পোজিশন ও স্লট
কম্পোজিশন হল শ্যাডো DOM-এর এমন একটি ফিচার যা সবচেয়ে কম বোঝা যায়, তবে এটি সম্ভবত সবচেয়ে গুরুত্বপূর্ণ।
আমাদের ওয়েব ডেভেলপমেন্টের দুনিয়ায়, কম্পোজিশন হল এমন একটি পদ্ধতি যার মাধ্যমে আমরা অ্যাপ তৈরি করি,
HTML-এর বাইরে ডিক্লেরেটিভভাবে। বিভিন্ন বিল্ডিং ব্লক (<div>, <header>,
<form>, <input>) একসাথে এসে অ্যাপ তৈরি করে। এমনকি, এইসব ট্যাগের মধ্যে কয়েকটি
একে অপরের সাথে কাজ করে। কম্পোজিশনের জন্যই <select>,
<details>, <form> ও <video>-এর মতো নেটিভ এলিমেন্ট এত নমনীয়। এই ট্যাগগুলির প্রতিটি চিলড্রেন হিসেবে নির্দিষ্ট HTML গ্রহণ করে এবং তাদের সাথে বিশেষ কিছু করে। যেমন,
<select> জানে কীভাবে <option> এবং <optgroup> ড্রপডাউন এবং
মাল্টি-সিলেক্ট উইজেটে রেন্ডার করতে হয়। <details> এলিমেন্ট <summary>-কে বড় করা যায় এমন তীরচিহ্ন হিসেবে
রেন্ডার করে। এমনকি <video> জানে কীভাবে নির্দিষ্ট চিলড্রেন এলিমেন্ট নিয়ে কাজ করতে হয়:
<source> এলিমেন্ট রেন্ডার করা হয় না, তবে সেগুলি ভিডিওর আচরণকে প্রভাবিত করে।
কী জাদু!
পরিভাষা: লাইট DOM বনাম শ্যাডো DOM
Shadow DOM কম্পোজিশন ওয়েব ডেভেলপমেন্টে একগুচ্ছ নতুন ফান্ডামেন্টাল নিয়ে আসে। বিস্তারিত আলোচনা করার আগে, আসুন কিছু পরিভাষা স্ট্যান্ডার্ডাইজ করি যাতে আমরা একই ভাষায় কথা বলতে পারি।
লাইট DOM
আপনার কম্পোনেন্টের ব্যবহারকারীর লেখা মার্কআপ। এই DOM, কম্পোনেন্টের শ্যাডো DOM-এর বাইরে থাকে। এটি এলিমেন্টের আসল চিলড্রেন।
<better-button>
<!-- the image and span are better-button's light DOM -->
<img src="gear.svg" slot="icon">
<span>Settings</span>
</better-button>
Shadow DOM
DOM কম্পোনেন্ট লেখক লেখেন। Shadow DOM হল কম্পোনেন্টের লোকাল এবং এটি ইন্টার্নাল স্ট্রাকচার, স্কোপড CSS এবং আপনার ইমপ্লিমেন্টেশনের বিবরণ এনক্যাপসুলেট করে। আপনার কম্পোনেন্টের গ্রাহকের দ্বারা তৈরি করা মার্কআপ কীভাবে রেন্ডার করতে হবে তাও এটি নির্ধারণ করতে পারে।
#shadow-root
<style>...</style>
<slot name="icon"></slot>
<span id="wrapper">
<slot>Button</slot>
</span>
ফ্ল্যাটেন করা DOM ট্রি
ব্রাউজার ব্যবহারকারীর লাইট DOM আপনার শ্যাডো DOM-এ ডিস্ট্রিবিউট করার ফলাফল, যা ফাইনাল প্রোডাক্ট রেন্ডার করে। DevTools-এ আপনি শেষ পর্যন্ত যা দেখেন এবং পৃষ্ঠায় যা রেন্ডার করা হয় তা হল ফ্ল্যাটেন করা ট্রি।
<better-button>
#shadow-root
<style>...</style>
<slot name="icon">
<img src="gear.svg" slot="icon">
</slot>
<span id="wrapper">
<slot>
<span>Settings</span>
</slot>
</span>
</better-button>
<slot> এলিমেন্ট
Shadow DOM, <slot> এলিমেন্ট ব্যবহার করে বিভিন্ন DOM ট্রি একসাথে কম্পোজ করে।
স্লট হল আপনার কম্পোনেন্টের মধ্যে থাকা প্লেসহোল্ডার যা ব্যবহারকারীরা তাদের
নিজস্ব মার্কআপ দিয়ে পূরণ করতে পারবেন। এক বা একাধিক স্লট নির্ধারণ করার মাধ্যমে, আপনি আপনার কম্পোনেন্টের শ্যাডো DOM-এ
রেন্ডার করার জন্য বাইরের মার্কআপকে আমন্ত্রণ জানান। আসলে, আপনি বলছেন "ব্যবহারকারীর মার্কআপ
এখানে রেন্ডার করুন"।
<slot> আমন্ত্রণ জানালে এলিমেন্টকে শ্যাডো DOM বাউন্ডারি "ক্রস" করার অনুমতি দেওয়া হয়। এইসব এলিমেন্টকে ডিস্ট্রিবিউটেড নোড বলা হয়। ধারণাগতভাবে,
ডিস্ট্রিবিউটেড নোডগুলি একটু অদ্ভুত বলে মনে হতে পারে। স্লট ফিজিক্যালি DOM সরায় না; এটি
শ্যাডো DOM-এর মধ্যে অন্য লোকেশনে রেন্ডার করে।
কোনও কম্পোনেন্ট তার শ্যাডো DOM-এ শূন্য বা তার বেশি স্লট নির্ধারণ করতে পারে। স্লট খালি থাকতে পারে অথবা ফলব্যাক কন্টেন্ট প্রদান করতে পারে। ব্যবহারকারী লাইট DOM কন্টেন্ট প্রদান না করলে, স্লট তার ফলব্যাক কন্টেন্ট রেন্ডার করে।
<!-- Default slot. If there's more than one default slot, the first is used. -->
<slot></slot>
<slot>fallback content</slot> <!-- default slot with fallback content -->
<slot> <!-- default slot entire DOM tree as fallback -->
<h2>Title</h2>
<summary>Description text</summary>
</slot>
এছাড়াও, আপনি নাম দেওয়া স্লট তৈরি করতে পারবেন। নামযুক্ত স্লট হল আপনার শ্যাডো DOM-এর নির্দিষ্ট হোল যা ব্যবহারকারীরা নাম দিয়ে রেফার করেন।
উদাহরণ - <fancy-tabs>-এর শ্যাডো DOM-এ স্লট:
#shadow-root
<div id="tabs">
<slot id="tabsSlot" name="title"></slot> <!-- named slot -->
</div>
<div id="panels">
<slot id="panelsSlot"></slot>
</div>
কম্পোনেন্ট ব্যবহারকারীরা <fancy-tabs> এইভাবে ঘোষণা করেন:
<fancy-tabs>
<button slot="title">Title</button>
<button slot="title" selected>Title 2</button>
<button slot="title">Title 3</button>
<section>content panel 1</section>
<section>content panel 2</section>
<section>content panel 3</section>
</fancy-tabs>
<!-- Using <h2>'s and changing the ordering would also work! -->
<fancy-tabs>
<h2 slot="title">Title</h2>
<section>content panel 1</section>
<h2 slot="title" selected>Title 2</h2>
<section>content panel 2</section>
<h2 slot="title">Title 3</h2>
<section>content panel 3</section>
</fancy-tabs>
আপনি যদি ভাবছেন, তাহলে ফ্ল্যাটেন করা ট্রি দেখতে কিছুটা এইরকম হয়:
<fancy-tabs>
#shadow-root
<div id="tabs">
<slot id="tabsSlot" name="title">
<button slot="title">Title</button>
<button slot="title" selected>Title 2</button>
<button slot="title">Title 3</button>
</slot>
</div>
<div id="panels">
<slot id="panelsSlot">
<section>content panel 1</section>
<section>content panel 2</section>
<section>content panel 3</section>
</slot>
</div>
</fancy-tabs>
লক্ষ্য করুন যে আমাদের কম্পোনেন্ট বিভিন্ন কনফিগারেশন ম্যানেজ করতে পারে, কিন্তু
ফ্ল্যাট করা DOM ট্রি একই থাকে। এছাড়াও, আমরা <button> থেকে
<h2>-এ বদল করে নিতে পারি। এই কম্পোনেন্টটি বিভিন্ন ধরনের চিলড্রেন হ্যান্ডেল করার জন্য তৈরি করা হয়েছে… ঠিক
<select>-এর মতো!
স্টাইল বেছে নেওয়া
ওয়েব কম্পোনেন্ট স্টাইল করার জন্য অনেক বিকল্প আছে। শ্যাডো DOM ব্যবহার করে এমন কোনও কম্পোনেন্ট মূল পৃষ্ঠার মাধ্যমে স্টাইল করা যেতে পারে, নিজস্ব স্টাইল নির্ধারণ করতে পারে অথবা ডিফল্ট ওভাররাইড করার জন্য ব্যবহারকারীদের হুক (CSS কাস্টম প্রপার্টির আকারে) প্রদান করতে পারে।
কম্পোনেন্ট-ডিফাইন্ড স্টাইল
শ্যাডো DOM-এর সবচেয়ে উপযোগী ফিচার হল স্কোপড CSS:
- বাইরের পৃষ্ঠার CSS বাছাইকারী আপনার কম্পোনেন্টের মধ্যে প্রযোজ্য হয় না।
- ভিতরে নির্দিষ্ট করা স্টাইল বাইরে দেখা যায় না। সেগুলি হোস্ট এলিমেন্টে স্কোপ করা হয়।
শ্যাডো DOM-এর মধ্যে ব্যবহৃত CSS বাছাইকারী আপনার কম্পোনেন্টে লোকালভাবে প্রয়োগ করা হয়। ব্যবহারিক ক্ষেত্রে, এর অর্থ হল, পৃষ্ঠার অন্য কোথাও কোনও দ্বন্দ্বের ব্যাপারে চিন্তা না করেই আমরা আবার সাধারণ আইডি/ক্লাসের নাম ব্যবহার করতে পারি। Shadow DOM-এর মধ্যে সহজ CSS সিলেক্টর ব্যবহার করা পেশাদার পদ্ধতি হিসেবে বিবেচিত হয়। এগুলি পারফর্ম্যান্সের জন্যও ভালো।
উদাহরণ - শ্যাডো রুটে সংজ্ঞায়িত স্টাইলগুলি লোকাল
#shadow-root
<style>
#panels {
box-shadow: 0 2px 2px rgba(0, 0, 0, .3);
background: white;
...
}
#tabs {
display: inline-flex;
...
}
</style>
<div id="tabs">
...
</div>
<div id="panels">
...
</div>
স্টাইলশিটও শ্যাডো ট্রিতে স্কোপ করা হয়:
#shadow-root
<link rel="stylesheet" href="styles.css">
<div id="tabs">
...
</div>
<div id="panels">
...
</div>
আপনি কি কখনও ভেবে দেখেছেন যে আপনি যখন multiple অ্যাট্রিবিউট যোগ করেন তখন <select> এলিমেন্ট কীভাবে একটি মাল্টি-সিলেক্ট উইজেট (ড্রপডাউনের পরিবর্তে) রেন্ডার করে:
<select multiple>
<option>Do</option>
<option selected>Re</option>
<option>Mi</option>
<option>Fa</option>
<option>So</option>
</select>
আপনি যে অ্যাট্রিবিউট ঘোষণা করেন তার উপর ভিত্তি করে <select> নিজেকে আলাদাভাবে স্টাইল করতে পারে। :host
সিলেক্টর ব্যবহার করে ওয়েব কম্পোনেন্টও নিজেদের স্টাইল করতে পারে।
উদাহরণ - একটি কম্পোনেন্ট নিজেই স্টাইল করছে
<style>
:host {
display: block; /* by default, custom elements are display: inline */
contain: content; /* CSS containment FTW. */
}
</style>
:host-এর ক্ষেত্রে একটি সমস্যা হল, মূল পৃষ্ঠার নিয়মগুলি এলিমেন্টে সংজ্ঞায়িত :host নিয়মের চেয়ে
বেশি নির্দিষ্ট। অর্থাৎ, বাইরের স্টাইল প্রাধান্য পায়। এটি
ব্যবহারকারীদের বাইরে থেকে আপনার টপ-লেভেল স্টাইলিং ওভাররাইড করার অনুমতি দেয়। এছাড়াও, :host
শুধুমাত্র শ্যাডো রুটের প্রসঙ্গে কাজ করে, তাই আপনি এটি শ্যাডো DOM-এর বাইরে
ব্যবহার করতে পারবেন না।
:host(<selector>)-এর ফাংশনাল ফর্ম আপনাকে হোস্টকে টার্গেট করার অনুমতি দেয় যদি এটি
<selector>-এর সাথে মেলে। এটি আপনার কম্পোনেন্টের জন্য ব্যবহারকারীর ইন্টারঅ্যাকশনে প্রতিক্রিয়া দেখায় এমন আচরণগুলিকে এনক্যাপসুলেট করার একটি দুর্দান্ত উপায়
বা হোস্টের উপর ভিত্তি করে অভ্যন্তরীণ নোডগুলিকে স্টেট বা স্টাইল করে।
<style>
:host {
opacity: 0.4;
will-change: opacity;
transition: opacity 300ms ease-in-out;
}
:host(:hover) {
opacity: 1;
}
:host([disabled]) { /* style when host has disabled attribute. */
background: grey;
pointer-events: none;
opacity: 0.4;
}
:host(.blue) {
color: blue; /* color host when it has class="blue" */
}
:host(.pink) > #tabs {
color: pink; /* color internal #tabs node when host has class="pink". */
}
</style>
প্রসঙ্গের উপর ভিত্তি করে স্টাইলিং
:host-context(<selector>) কম্পোনেন্টের সাথে ম্যাচ করে যদি এটি বা এর কোনও পূর্বপুরুষ
<selector>-এর সাথে ম্যাচ করে। এর একটি সাধারণ ব্যবহার হল কোনও কম্পোনেন্টের আশেপাশের পরিবেশের উপর ভিত্তি করে থিমিং করা। যেমন, অনেক লোক <html> বা <body>-এ ক্লাস প্রয়োগ করে থিমিং করেন:
<body class="darktheme">
<fancy-tabs>
...
</fancy-tabs>
</body>
:host-context(.darktheme), <fancy-tabs>-কে স্টাইল করবে যখন এটি .darktheme-এর
বংশধর হবে:
:host-context(.darktheme) {
color: white;
background: black;
}
:host-context() থিমিংয়ের জন্য উপযোগী হতে পারে, তবে আরও ভালো পদ্ধতি হল
CSS কাস্টম প্রপার্টি ব্যবহার করে স্টাইল হুক তৈরি করা।
ডিস্ট্রিবিউটেড নোড স্টাইল করা
::slotted(<compound-selector>) এমন নোডের সাথে ম্যাচ করে যা
<slot>-এ ডিস্ট্রিবিউট করা হয়েছে।
ধরা যাক, আমরা একটি নাম ব্যাজ কম্পোনেন্ট তৈরি করেছি:
<name-badge>
<h2>Eric Bidelman</h2>
<span class="title">
Digital Jedi, <span class="company">Google</span>
</span>
</name-badge>
কম্পোনেন্টের শ্যাডো DOM ব্যবহারকারীর <h2> ও .title স্টাইল করতে পারে:
<style>
::slotted(h2) {
margin: 0;
font-weight: 300;
color: red;
}
::slotted(.title) {
color: orange;
}
/* DOESN'T WORK (can only select top-level nodes).
::slotted(.company),
::slotted(.title .company) {
text-transform: uppercase;
}
*/
</style>
<slot></slot>
আপনার আগে থেকে মনে থাকলে, <slot> ব্যবহারকারীর লাইট DOM সরায় না। যখন
নোডগুলি একটি <slot>-এ বিতরণ করা হয়, তখন <slot> তাদের DOM রেন্ডার করে কিন্তু
নোডগুলি শারীরিকভাবে থেকে যায়। ডিস্ট্রিবিউশনের আগে প্রয়োগ করা স্টাইল ডিস্ট্রিবিউশনের পরেও
প্রয়োগ করা হয়। তবে, লাইট DOM ডিস্ট্রিবিউট করা হলে, এটি অতিরিক্ত স্টাইল (শ্যাডো DOM দ্বারা নির্ধারিত)
নিতে পারে।
<fancy-tabs> থেকে আরও একটি বিস্তারিত উদাহরণ:
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `
<style>
#panels {
box-shadow: 0 2px 2px rgba(0, 0, 0, .3);
background: white;
border-radius: 3px;
padding: 16px;
height: 250px;
overflow: auto;
}
#tabs {
display: inline-flex;
-webkit-user-select: none;
user-select: none;
}
#tabsSlot::slotted(*) {
font: 400 16px/22px 'Roboto';
padding: 16px 8px;
...
}
#tabsSlot::slotted([aria-selected="true"]) {
font-weight: 600;
background: white;
box-shadow: none;
}
#panelsSlot::slotted([aria-hidden="true"]) {
display: none;
}
</style>
<div id="tabs">
<slot id="tabsSlot" name="title"></slot>
</div>
<div id="panels">
<slot id="panelsSlot"></slot>
</div>
`;
এই উদাহরণে, দুটি স্লট আছে: ট্যাব টাইটেলের জন্য একটি নামযুক্ত স্লট এবং
ট্যাব প্যানেল কন্টেন্টের জন্য একটি স্লট। ব্যবহারকারী কোনও ট্যাব বেছে নিলে, আমরা তার বেছে নেওয়া ট্যাবটি বোল্ড করি
এবং এর প্যানেল দেখাই। এটি করার জন্য, selected অ্যাট্রিবিউট আছে এমন ডিস্ট্রিবিউটেড নোড বেছে নিতে হবে। কাস্টম এলিমেন্টের JS (এখানে দেখানো হয়নি) সঠিক সময়ে সেই অ্যাট্রিবিউট
যোগ করে।
বাইরে থেকে কোনও কম্পোনেন্ট স্টাইল করা
বাইরে থেকে কোনও কম্পোনেন্ট স্টাইল করার কয়েকটি উপায় আছে। সবচেয়ে সহজ উপায় হল, সিলেক্টর হিসেবে ট্যাগের নাম ব্যবহার করা:
fancy-tabs {
width: 500px;
color: red; /* Note: inheritable CSS properties pierce the shadow DOM boundary. */
}
fancy-tabs:hover {
box-shadow: 0 3px 3px #ccc;
}
শ্যাডো DOM-এ সংজ্ঞায়িত স্টাইলের চেয়ে বাইরের স্টাইল সবসময় বেশি প্রাধান্য পায়। যেমন,
ব্যবহারকারী যদি সিলেক্টর fancy-tabs { width: 500px; } লেখেন, তাহলে এটি
কম্পোনেন্টের নিয়মকে ওভাররাইড করবে: :host { width: 650px;}।
কম্পোনেন্ট স্টাইল করে আপনি খুব বেশি কিছু করতে পারবেন না। কিন্তু আপনি যদি কোনও কম্পোনেন্টের ইন্টার্নাল স্টাইল করতে চান, তাহলে কী হবে? এর জন্য, আমাদের CSS কাস্টম প্রপার্টি প্রয়োজন।
CSS কাস্টম প্রপার্টি ব্যবহার করে স্টাইল হুক তৈরি করা
কম্পোনেন্টের লেখক CSS কাস্টম প্রপার্টি ব্যবহার করে স্টাইলিং হুক প্রদান করলে, ব্যবহারকারীরা ইন্টার্নাল স্টাইল টুইক করতে পারবেন। ধারণাগতভাবে, আইডিয়াটি
<slot>-এর মতো। আপনি ব্যবহারকারীদের ওভাররাইড করার জন্য "স্টাইল প্লেসহোল্ডার" তৈরি করেন।
উদাহরণ - <fancy-tabs> ব্যবহারকারীদের ব্যাকগ্রাউন্ডের রঙ ওভাররাইড করার অনুমতি দেয়:
<!-- main page -->
<style>
fancy-tabs {
margin-bottom: 32px;
--fancy-tabs-bg: black;
}
</style>
<fancy-tabs background>...</fancy-tabs>
এর শ্যাডো DOM-এর মধ্যে:
:host([background]) {
background: var(--fancy-tabs-bg, #9E9E9E);
border-radius: 10px;
padding: 10px;
}
এই ক্ষেত্রে, কম্পোনেন্ট ব্যাকগ্রাউন্ড ভ্যালু হিসেবে black ব্যবহার করবে কারণ ব্যবহারকারী এটি প্রদান করেছেন। অন্যথায়, এটি ডিফল্ট হিসেবে #9E9E9E-এ সেট করা হবে।
উন্নত বিষয়
ক্লোজড শ্যাডো রুট তৈরি করা (এড়িয়ে যাওয়া উচিত)
শ্যাডো DOM-এর আরেকটি ফ্লেভার আছে, যাকে "বন্ধ" মোড বলা হয়। আপনি যখন একটি
বন্ধ শ্যাডো ট্রি তৈরি করেন, তখন বাইরের জাভাস্ক্রিপ্ট আপনার কম্পোনেন্টের অভ্যন্তরীণ DOM অ্যাক্সেস করতে পারবে না। এটি <video>-এর মতো নেটিভ এলিমেন্ট যেভাবে কাজ করে তার মতোই।
JavaScript, <video>-এর শ্যাডো DOM অ্যাক্সেস করতে পারছে না কারণ ব্রাউজার
এটি ক্লোজড-মোড শ্যাডো রুট ব্যবহার করে প্রয়োগ করে।
উদাহরণ - একটি বন্ধ শ্যাডো ট্রি তৈরি করা:
const div = document.createElement('div');
const shadowRoot = div.attachShadow({mode: 'closed'}); // close shadow tree
// div.shadowRoot === null
// shadowRoot.host === div
ক্লোজড-মোডের কারণে অন্যান্য API-ও প্রভাবিত হয়:
Element.assignedSlot/TextNode.assignedSlotরিটার্নnull- শ্যাডো DOM-এর মধ্যে থাকা এলিমেন্টের সাথে যুক্ত ইভেন্টের জন্য
Event.composedPath(), [] রিটার্ন করে
{mode: 'closed'}-এর মাধ্যমে আপনার কেন কখনও ওয়েব কম্পোনেন্ট তৈরি করা উচিত নয়, তার সারসংক্ষেপ এখানে দেওয়া হল:
নিরাপত্তার কৃত্রিম অনুভূতি।
Element.prototype.attachShadowহাইজ্যাক করা থেকে আক্রমণকারীকে আটকানোর কোনও উপায় নেই।ক্লোজড মোড আপনার কাস্টম এলিমেন্ট কোডকে নিজস্ব শ্যাডো DOM অ্যাক্সেস করা থেকে আটকায়। সেটা সম্পূর্ণ ব্যর্থতা। পরিবর্তে, আপনি যদি
querySelector()-এর মতো জিনিস ব্যবহার করতে চান, তাহলে আপনাকে পরে ব্যবহারের জন্য একটি রেফারেন্স স্ট্যাশ করতে হবে। এটি সম্পূর্ণভাবে ক্লোজড মোডের আসল উদ্দেশ্যকে পরাজিত করে!customElements.define('x-element', class extends HTMLElement { constructor() { super(); // always call super() first in the constructor. this._shadowRoot = this.attachShadow({mode: 'closed'}); this._shadowRoot.innerHTML = '<div class="wrapper"></div>'; } connectedCallback() { // When creating closed shadow trees, you'll need to stash the shadow root // for later if you want to use it again. Kinda pointless. const wrapper = this._shadowRoot.querySelector('.wrapper'); } ... });ক্লোজড মোড আপনার কম্পোনেন্টকে ব্যবহারকারীর জন্য কম নমনীয় করে তোলে। আপনি ওয়েব কম্পোনেন্ট তৈরি করার সময়, এমন সময় আসবে যখন আপনি কোনও ফিচার যোগ করতে ভুলে যাবেন। একটি কনফিগারেশন বিকল্প। ব্যবহারকারী যে ধরনের ব্যবহার চান। একটি সাধারণ উদাহরণ হল ইন্টার্নাল নোডের জন্য পর্যাপ্ত স্টাইলিং হুক অন্তর্ভুক্ত করতে ভুলে যাওয়া। ক্লোজড মোডে, ব্যবহারকারীদের ডিফল্ট ওভাররাইড করার এবং স্টাইল পরিবর্তন করার কোনও উপায় নেই। কম্পোনেন্টের ইন্টার্নাল অ্যাক্সেস করতে পারাটা খুবই সহায়ক। শেষ পর্যন্ত, ব্যবহারকারীরা আপনার কম্পোনেন্ট ফর্ক করবেন, অন্য একটি খুঁজে নেবেন বা তাদের নিজস্ব কম্পোনেন্ট তৈরি করবেন যদি এটি তাদের চাহিদা পূরণ না করে :(
JS-এ স্লট নিয়ে কাজ করা
Shadow DOM API স্লট ও ডিস্ট্রিবিউট করা নোডের সাথে কাজ করার জন্য ইউটিলিটি প্রদান করে। কাস্টম এলিমেন্ট তৈরি করার সময় এগুলি কাজে লাগে।
slotchange ইভেন্ট
স্লটের ডিস্ট্রিবিউট করা নোড পরিবর্তন হলে slotchange ইভেন্ট ফায়ার হয়। যেমন, ব্যবহারকারী যদি লাইট DOM থেকে চাইল্ড যোগ করেন/সরিয়ে দেন।
const slot = this.shadowRoot.querySelector('#slot');
slot.addEventListener('slotchange', e => {
console.log('light dom children changed!');
});
লাইট DOM-এ অন্যান্য ধরনের পরিবর্তন মনিটর করতে, আপনার এলিমেন্টের কনস্ট্রাক্টরে একটি
MutationObserver
সেট-আপ করতে পারেন।
একটি স্লটে কোন কোন এলিমেন্ট রেন্ডার করা হচ্ছে?
কোনও স্লটের সাথে কোন কোন এলিমেন্ট যুক্ত আছে তা জানা কখনও কখনও কাজে লাগে। স্লটটি কোন এলিমেন্ট রেন্ডার করছে তা খুঁজে পেতে
slot.assignedNodes() কল করুন। এছাড়াও,
{flatten: true} বিকল্পটি স্লটের ফলব্যাক কন্টেন্ট ফিরিয়ে দেবে (যদি কোনও নোড
ডিস্ট্রিবিউট করা না হয়)।
উদাহরণস্বরূপ, ধরা যাক আপনার শ্যাডো DOM দেখতে এইরকম:
<slot><b>fallback content</b></slot>
| ব্যবহার | কল করুন | ফলাফল |
|---|---|---|
| <my-component>কম্পোনেন্ট টেক্সট</my-component> | slot.assignedNodes(); |
[component text] |
| <my-component></my-component> | slot.assignedNodes(); |
[] |
| <my-component></my-component> | slot.assignedNodes({flatten: true}); |
[<b>fallback content</b>] |
কোন স্লটে একটি এলিমেন্ট অ্যাসাইন করা হয়েছে?
বিপরীত প্রশ্নের উত্তর দেওয়াও সম্ভব। element.assignedSlot আপনাকে
জানায় যে আপনার এলিমেন্ট কোন কম্পোনেন্ট স্লটে অ্যাসাইন করা হয়েছে।
Shadow DOM ইভেন্ট মডেল
শ্যাডো DOM থেকে কোনও ইভেন্ট বাবল আপ হলে, শ্যাডো DOM-এর এনক্যাপসুলেশন বজায় রাখার জন্য এর টার্গেট অ্যাডজাস্ট করা হয়। অর্থাৎ, ইভেন্টগুলি এমনভাবে রি-টার্গেট করা হয় যাতে মনে হয় যে সেগুলি আপনার শ্যাডো DOM-এর মধ্যে থাকা ইন্টার্নাল এলিমেন্টের পরিবর্তে কম্পোনেন্ট থেকে এসেছে। কিছু ইভেন্ট শ্যাডো DOM-এর বাইরেও প্রোপাগেট করে না।
যেসব ইভেন্ট শ্যাডো বাউন্ডারি ক্রস করে, সেগুলি হল:
- ফোকাস ইভেন্ট:
blur,focus,focusin,focusout - মাউস ইভেন্ট:
click,dblclick,mousedown,mouseenter,mousemove, ইত্যাদি। - হুইল ইভেন্ট:
wheel - ইনপুট ইভেন্ট:
beforeinput,input - কীবোর্ড ইভেন্ট:
keydown,keyup - কম্পোজিশন ইভেন্ট:
compositionstart,compositionupdate,compositionend - DragEvent:
dragstart,drag,dragend,drop, ইত্যাদি।
পরামর্শ
শ্যাডো ট্রি খোলা থাকলে, event.composedPath() কল করলে ইভেন্ট যে নোডের মাধ্যমে
ট্রাভেল করেছে তার অ্যারে রিটার্ন করবে।
কাস্টম ইভেন্ট ব্যবহার করা
শ্যাডো ট্রির ইন্টার্নাল নোডে ফায়ার করা কাস্টম DOM ইভেন্ট শ্যাডো বাউন্ডারির বাইরে
বাবল আউট হয় না, যদি না ইভেন্টটি composed: true ফ্ল্যাগ ব্যবহার করে
তৈরি করা হয়:
// Inside <fancy-tab> custom element class definition:
selectTab() {
const tabs = this.shadowRoot.querySelector('#tabs');
tabs.dispatchEvent(new Event('tab-select', {bubbles: true, composed: true}));
}
composed: false (ডিফল্ট) হলে, গ্রাহকরা আপনার শ্যাডো রুটের বাইরে ইভেন্ট শুনতে পারবেন না।
<fancy-tabs></fancy-tabs>
<script>
const tabs = document.querySelector('fancy-tabs');
tabs.addEventListener('tab-select', e => {
// won't fire if `tab-select` wasn't created with `composed: true`.
});
</script>
ফোকাস ম্যানেজ করা
শ্যাডো DOM-এর ইভেন্ট মডেল থেকে আপনার মনে থাকলে, শ্যাডো DOM-এর মধ্যে
ফায়ার করা ইভেন্টগুলি এমনভাবে অ্যাডজাস্ট করা হয় যাতে মনে হয় সেগুলি হোস্টিং এলিমেন্ট থেকে এসেছে।
যেমন, ধরা যাক আপনি শ্যাডো রুটের মধ্যে থাকা কোনও <input>-এ ক্লিক করেছেন:
<x-focus>
#shadow-root
<input type="text" placeholder="Input inside shadow dom">
focus ইভেন্টটি দেখে মনে হবে যে এটি <x-focus> থেকে এসেছে, <input> থেকে নয়।
একইভাবে, document.activeElement-এর জন্য <x-focus> দিতে হবে। যদি শ্যাডো রুট
mode:'open' দিয়ে তৈরি করা হয় (ক্লোজড মোড দেখুন), তাহলে আপনি
ফোকাস পাওয়া ইন্টার্নাল নোডও অ্যাক্সেস করতে পারবেন:
document.activeElement.shadowRoot.activeElement // only works with open mode.
যদি শ্যাডো DOM-এর একাধিক লেভেল থাকে (যেমন, অন্য একটি কাস্টম এলিমেন্টের মধ্যে
একটি কাস্টম এলিমেন্ট), তাহলে activeElement খুঁজে পেতে আপনাকে শ্যাডো রুটের মধ্যে
বারবার ড্রিল করতে হবে:
function deepActiveElement() {
let a = document.activeElement;
while (a && a.shadowRoot && a.shadowRoot.activeElement) {
a = a.shadowRoot.activeElement;
}
return a;
}
ফোকাস করার আরেকটি বিকল্প হল delegatesFocus: true বিকল্প, যা শ্যাডো ট্রির মধ্যে এলিমেন্টের ফোকাস আচরণকে প্রসারিত করে:
- আপনি যদি শ্যাডো DOM-এর মধ্যে কোনও নোডে ক্লিক করেন এবং নোডটি ফোকাস করার মতো জায়গা না হয়, তাহলে প্রথম ফোকাস করার মতো জায়গাটি ফোকাস করা হয়।
- শ্যাডো DOM-এর মধ্যে কোনও নোড ফোকাস পেলে, ফোকাস করা এলিমেন্টের পাশাপাশি হোস্টে
:focusপ্রয়োগ করা হয়।
উদাহরণ - delegatesFocus: true কীভাবে ফোকাস আচরণ পরিবর্তন করে
<style>
:focus {
outline: 2px solid red;
}
</style>
<x-focus></x-focus>
<script>
customElements.define('x-focus', class extends HTMLElement {
constructor() {
super(); // always call super() first in the constructor.
const root = this.attachShadow({mode: 'open', delegatesFocus: true});
root.innerHTML = `
<style>
:host {
display: flex;
border: 1px dotted black;
padding: 16px;
}
:focus {
outline: 2px solid blue;
}
</style>
<div>Clickable Shadow DOM text</div>
<input type="text" placeholder="Input inside shadow dom">`;
// Know the focused element inside shadow DOM:
this.addEventListener('focus', function(e) {
console.log('Active element (inside shadow dom):',
this.shadowRoot.activeElement);
});
}
});
</script>
ফলাফল
<x-focus>-এ ফোকাস করা হলে (ব্যবহারকারীর ক্লিক, ট্যাব করা,
focus(), ইত্যাদি), "ক্লিক করা যায় এমন শ্যাডো DOM টেক্সট"-এ ক্লিক করা হলে বা ইন্টার্নাল
<input>-এ ফোকাস করা হলে (autofocus সহ) উপরের ফলাফল পাওয়া যায়।
আপনি যদি delegatesFocus: false সেট করেন, তাহলে এর পরিবর্তে আপনি যা দেখতে পাবেন তা এখানে দেওয়া হল:
delegatesFocus: false এবং ইন্টার্নাল <input> ফোকাস করা হয়েছে।
delegatesFocus: false এবং <x-focus>
ফোকাস পায় (যেমন, এতে tabindex="0" আছে)।
delegatesFocus: false এবং "ক্লিক করা যায় এমন শ্যাডো DOM টেক্সট"
ক্লিক করা হয় (অথবা এলিমেন্টের শ্যাডো DOM-এর মধ্যে অন্য খালি জায়গায় ক্লিক করা হয়)।
পরামর্শ ও উপায়
গত কয়েক বছর ধরে আমি ওয়েব কম্পোনেন্ট তৈরি করার ব্যাপারে কিছু জিনিস শিখেছি। আমার মনে হয়, কম্পোনেন্ট তৈরি ও শ্যাডো DOM ডিবাগ করার জন্য আপনি এইসব পরামর্শের মধ্যে কিছু উপযোগী বলে মনে করবেন।
CSS কন্টেনমেন্ট ব্যবহার করুন
সাধারণত, কোনও ওয়েব কম্পোনেন্টের লেআউট/স্টাইল/পেইন্ট সম্পূর্ণভাবে স্বয়ংসম্পূর্ণ হয়। পারফর্ম্যান্সের
ক্ষেত্রে জয়ের জন্য :host-এ CSS কন্টেনমেন্ট ব্যবহার করুন:
<style>
:host {
display: block;
contain: content; /* Boom. CSS containment FTW. */
}
</style>
ইনহেরিটেবল স্টাইল রিসেট করা হচ্ছে
ইনহেরিট করা যায় এমন স্টাইল (background, color, font, line-height ইত্যাদি) শ্যাডো DOM-এ
ইনহেরিট করা চালিয়ে যায়। অর্থাৎ, ডিফল্ট হিসেবে এগুলি শ্যাডো DOM-এর সীমানা
ভেদ করে। আপনি যদি নতুন করে শুরু করতে চান, তাহলে all: initial; ব্যবহার করে
ইনহেরিটেবল স্টাইল শ্যাডো বাউন্ডারি ক্রস করলে সেগুলিকে প্রাথমিক ভ্যালুতে রিসেট করুন।
<style>
div {
padding: 10px;
background: red;
font-size: 25px;
text-transform: uppercase;
color: white;
}
</style>
<div>
<p>I'm outside the element (big/white)</p>
<my-element>Light DOM content is also affected.</my-element>
<p>I'm outside the element (big/white)</p>
</div>
<script>
const el = document.querySelector('my-element');
el.attachShadow({mode: 'open'}).innerHTML = `
<style>
:host {
all: initial; /* 1st rule so subsequent properties are reset. */
display: block;
background: white;
}
</style>
<p>my-element: all CSS properties are reset to their
initial value using <code>all: initial</code>.</p>
<slot></slot>
`;
</script>
কোনও পৃষ্ঠায় ব্যবহার করা সব কাস্টম এলিমেন্ট খোঁজা
পৃষ্ঠায় ব্যবহৃত কাস্টম এলিমেন্ট খুঁজে পেতে কখনও কখনও এটি কাজে লাগে। এটি করতে, আপনাকে পৃষ্ঠায় ব্যবহৃত সব এলিমেন্টের শ্যাডো DOM রিকার্সিভলি ট্রাভার্স করতে হবে।
const allCustomElements = [];
function isCustomElement(el) {
const isAttr = el.getAttribute('is');
// Check for <super-button> and <button is="super-button">.
return el.localName.includes('-') || isAttr && isAttr.includes('-');
}
function findAllCustomElements(nodes) {
for (let i = 0, el; el = nodes[i]; ++i) {
if (isCustomElement(el)) {
allCustomElements.push(el);
}
// If the element has shadow DOM, dig deeper.
if (el.shadowRoot) {
findAllCustomElements(el.shadowRoot.querySelectorAll('*'));
}
}
}
findAllCustomElements(document.querySelectorAll('*'));
<template> থেকে এলিমেন্ট তৈরি করা
.innerHTML ব্যবহার করে শ্যাডো রুট পপুলেট করার পরিবর্তে, আমরা একটি ডিক্লারেটিভ
<template> ব্যবহার করতে পারি। ওয়েব কম্পোনেন্টের স্ট্রাকচার ঘোষণা করার জন্য টেমপ্লেট হল আদর্শ প্লেসহোল্ডার।
"কাস্টম এলিমেন্ট: আবার ব্যবহার করা যায় এমন ওয়েব কম্পোনেন্ট তৈরি করা" লিঙ্কে উদাহরণ দেখুন।
ইতিহাস ও ব্রাউজার সংক্রান্ত সহায়তা
আপনি যদি গত কয়েক বছর ধরে ওয়েব কম্পোনেন্ট ফলো করে থাকেন, তাহলে আপনি
জানবেন যে Chrome 35+/Opera কিছু সময় ধরে শ্যাডো DOM-এর পুরনো ভার্সন
শিপ করছে। Blink কিছু সময়ের জন্য সমান্তরালভাবে দুটি ভার্সনেই কাজ করা চালিয়ে
যাবে। v0 স্পেসিফিকেশন শ্যাডো রুট তৈরি করার জন্য একটি ভিন্ন পদ্ধতি প্রদান করে
(v1-এর element.attachShadow-এর পরিবর্তে element.createShadowRoot)। পুরনো পদ্ধতি কল করা
v0 সিম্যান্টিক্সের সাথে একটি শ্যাডো রুট তৈরি করা চালিয়ে যায়, তাই বিদ্যমান v0
কোড ভাঙবে না।
আপনি যদি পুরনো v0 স্পেসিফিকেশনে আগ্রহী হন, তাহলে html5rocks নিবন্ধগুলি দেখুন: ১, ২, ৩. এছাড়াও, শ্যাডো DOM v0 ও v1-এর মধ্যে পার্থক্য নিয়ে একটি দারুণ তুলনা আছে।
ব্রাউজার সহায়তা
Shadow DOM v1 Chrome 53 (স্ট্যাটাস), Opera 40, Safari 10 ও Firefox 63-এ শিপ করা হয়েছে। Edge ডেভেলপমেন্ট শুরু করেছে।
শ্যাডো DOM শনাক্ত করার ফিচার ব্যবহার করতে, attachShadow আছে কিনা তা চেক করুন:
const supportsShadowDOMV1 = !!HTMLElement.prototype.attachShadow;
পলিফিল
ব্রাউজার সাপোর্ট ব্যাপকভাবে উপলভ্য না হওয়া পর্যন্ত, shadydom এবং shadycss পলিফিল আপনাকে v1 ফিচার দেয়। Shady DOM, Shadow DOM-এর DOM স্কোপিং এবং shadycss পলিফিলস CSS কাস্টম প্রপার্টি এবং নেটিভ API-এর প্রদান করা স্টাইল স্কোপিং নকল করে।
পলিফিল ইনস্টল করুন:
bower install --save webcomponents/shadydom
bower install --save webcomponents/shadycss
পলিফিল ব্যবহার করুন:
function loadScript(src) {
return new Promise(function(resolve, reject) {
const script = document.createElement('script');
script.async = true;
script.src = src;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
// Lazy load the polyfill if necessary.
if (!supportsShadowDOMV1) {
loadScript('/bower_components/shadydom/shadydom.min.js')
.then(e => loadScript('/bower_components/shadycss/shadycss.min.js'))
.then(e => {
// Polyfills loaded.
});
} else {
// Native shadow dom v1 support. Go to go!
}
আপনার স্টাইল কীভাবে শিম/স্কোপ করবেন সেই সংক্রান্ত নির্দেশাবলীর জন্য https://github.com/webcomponents/shadycss#usage দেখুন।
উপসংহার
প্রথমবারের মতো, আমাদের কাছে একটি API প্রিমিটিভ আছে যা সঠিক CSS স্কোপিং,
DOM স্কোপিং করে এবং এতে সত্যিকারের কম্পোজিশন আছে। কাস্টম এলিমেন্টের মতো অন্যান্য ওয়েব কম্পোনেন্ট API-এর সাথে
কম্বাইন করা শ্যাডো DOM, হ্যাক বা <iframe>-এর মতো পুরনো ব্যাগেজ ব্যবহার না করেই
প্রকৃতপক্ষে এনক্যাপসুলেট করা কম্পোনেন্ট তৈরি করার উপায় প্রদান করে।
আমাকে ভুল বুঝবেন না। Shadow DOM অবশ্যই একটি জটিল বিষয়! তবে এটি এমন এক পশু যার থেকে অনেক কিছু শেখার আছে। এটির সাথে কিছুটা সময় কাটান। এটি শিখুন এবং প্রশ্ন জিজ্ঞাসা করুন!
আরও পড়ুন
- Shadow DOM v1 ও v0-এর মধ্যে পার্থক্য
- WebKit ব্লগ থেকে "স্লট-ভিত্তিক শ্যাডো DOM API-এর সাথে পরিচয়"।
- ওয়েব কম্পোনেন্ট এবং মডিউলার CSS-এর ভবিষ্যৎ ফিলিপ ওয়ালটন-এর লেখা
- Google-এর WebFundamentals থেকে "কাস্টম এলিমেন্ট: আবার ব্যবহার করার মতো ওয়েব কম্পোনেন্ট তৈরি করা"।
- Shadow DOM v1 স্পেসিফিকেশন
- কাস্টম এলিমেন্ট v1 স্পেসিফিকেশন
প্রায়শই জিজ্ঞাসিত প্রশ্ন
আমি কি আজ Shadow DOM v1 ব্যবহার করতে পারব?
পলিফিলের সাথে, হ্যাঁ। ব্রাউজার সাপোর্ট দেখুন।
শ্যাডো DOM কী কী নিরাপত্তা ফিচার প্রদান করে?
Shadow DOM কোনও নিরাপত্তা ফিচার নয়। এটি CSS স্কোপিং এবং কম্পোনেন্টে DOM ট্রি
লুকিয়ে রাখার জন্য একটি লাইটওয়েট টুল। আপনি যদি সত্যিকারের নিরাপত্তা সীমানা চান,
তাহলে <iframe> ব্যবহার করুন।
কোনও ওয়েব কম্পোনেন্টকে কি শ্যাডো DOM ব্যবহার করতেই হবে?
না! আপনাকে শ্যাডো DOM ব্যবহার করে এমন ওয়েব কম্পোনেন্ট তৈরি করতে হবে না। তবে, Shadow DOM ব্যবহার করে কাস্টম এলিমেন্ট তৈরি করার অর্থ হল আপনি CSS স্কোপিং, DOM এনক্যাপসুলেশন এবং কম্পোজিশনের মতো ফিচারের সুবিধা নিতে পারবেন।
ওপেন ও ক্লোজড শ্যাডো রুটের মধ্যে পার্থক্য কী?
বন্ধ করা শ্যাডো রুট দেখুন।