ছায়া DOM v1 - স্বয়ংসম্পূর্ণ ওয়েব উপাদান

Shadow DOM ওয়েব ডেভেলপারদের ওয়েব উপাদানগুলির জন্য কম্পার্টমেন্টালাইজড DOM এবং CSS তৈরি করতে দেয়

ছায়া DOM ওয়েব অ্যাপ তৈরির ভঙ্গুরতা দূর করে। HTML, CSS এবং JS-এর বৈশ্বিক প্রকৃতি থেকে ভঙ্গুরতা আসে। বছরের পর বছর ধরে আমরা সমস্যাগুলি এড়ানোর জন্য প্রচুর সংখ্যক সরঞ্জাম আবিষ্কার করেছি উদাহরণস্বরূপ, আপনি যখন একটি নতুন HTML আইডি/ক্লাস ব্যবহার করেন, তখন এটি পৃষ্ঠার দ্বারা ব্যবহৃত একটি বিদ্যমান নামের সাথে বিরোধ করবে কিনা তা বলার অপেক্ষা রাখে না। সূক্ষ্ম বাগগুলি হামাগুড়ি দেয়, CSS নির্দিষ্টতা একটি বিশাল সমস্যা হয়ে দাঁড়ায় ( !important বিষয়!), স্টাইল নির্বাচকরা নিয়ন্ত্রণের বাইরে চলে যায়, এবং কর্মক্ষমতা ক্ষতিগ্রস্ত হতে পারে । তালিকা চলতে থাকে।

ছায়া DOM CSS এবং DOM ঠিক করে । এটি ওয়েব প্ল্যাটফর্মে স্কোপড শৈলী প্রবর্তন করে। টুলস বা নামকরণের প্রথা ছাড়া, আপনি মার্কআপ সহ CSS বান্ডিল করতে পারেন, বাস্তবায়নের বিবরণ লুকাতে পারেন এবং ভ্যানিলা জাভাস্ক্রিপ্টে লেখকের স্বয়ংসম্পূর্ণ উপাদানগুলি লুকাতে পারেন৷

ভূমিকা

Shadow DOM হল তিনটি ওয়েব কম্পোনেন্ট স্ট্যান্ডার্ডের মধ্যে একটি: HTML টেমপ্লেট , Shadow DOM এবং কাস্টম উপাদানHTML আমদানি তালিকার অংশ ছিল কিন্তু এখন অবচয় বলে বিবেচিত হয়৷

ছায়া DOM ব্যবহার করে এমন ওয়েব কম্পোনেন্ট লিখতে হবে না। কিন্তু যখন আপনি তা করেন, তখন আপনি এর সুবিধাগুলি (CSS স্কোপিং, DOM এনক্যাপসুলেশন, কম্পোজিশন) ব্যবহার করেন এবং পুনর্ব্যবহারযোগ্য কাস্টম উপাদানগুলি তৈরি করেন, যা স্থিতিস্থাপক, অত্যন্ত কনফিগারযোগ্য এবং অত্যন্ত পুনঃব্যবহারযোগ্য। যদি কাস্টম উপাদানগুলি একটি নতুন এইচটিএমএল (জেএস এপিআই সহ) তৈরি করার উপায় হয়, তবে ছায়া DOM হল আপনি এটির HTML এবং CSS প্রদান করার উপায়৷ দুটি API স্বয়ংসম্পূর্ণ HTML, CSS এবং JavaScript সহ একটি উপাদান তৈরি করতে একত্রিত হয়।

Shadow DOM কে কম্পোনেন্ট-ভিত্তিক অ্যাপ তৈরির জন্য একটি টুল হিসেবে ডিজাইন করা হয়েছে। অতএব, এটি ওয়েব ডেভেলপমেন্টে সাধারণ সমস্যার সমাধান নিয়ে আসে:

  • বিচ্ছিন্ন DOM : একটি কম্পোনেন্টের DOM স্বয়ংসম্পূর্ণ (যেমন document.querySelector() কম্পোনেন্টের ছায়া DOM-এ নোড ফেরত দেবে না)।
  • স্কোপড CSS : ছায়া DOM এর ভিতরে সংজ্ঞায়িত CSS এর স্কোপ করা হয়। শৈলীর নিয়মগুলি ফাঁস হয় না এবং পৃষ্ঠা শৈলীগুলি রক্তপাত করে না।
  • রচনা : আপনার উপাদানের জন্য একটি ঘোষণামূলক, মার্কআপ-ভিত্তিক API ডিজাইন করুন।
  • সিএসএসকে সহজ করে - স্কোপড ডম মানে আপনি সাধারণ সিএসএস নির্বাচক, আরও জেনেরিক আইডি/শ্রেণীর নাম ব্যবহার করতে পারেন এবং নামকরণের দ্বন্দ্ব নিয়ে চিন্তা করবেন না।
  • উৎপাদনশীলতা - একটি বৃহৎ (গ্লোবাল) পৃষ্ঠার পরিবর্তে DOM-এর অংশে অ্যাপের কথা ভাবুন।

fancy-tabs ডেমো

এই নিবন্ধটি জুড়ে, আমি একটি ডেমো উপাদান ( <fancy-tabs> ) উল্লেখ করব এবং এটি থেকে কোড স্নিপেটগুলি উল্লেখ করব। যদি আপনার ব্রাউজার এপিআই সমর্থন করে, তাহলে আপনি এটির একটি লাইভ ডেমো দেখতে পাবেন। অন্যথায়, Github-এ সম্পূর্ণ উৎস দেখুন।

Github-এ উৎস দেখুন

ছায়া DOM কি?

DOM-এ পটভূমি

HTML ওয়েবকে শক্তি দেয় কারণ এটির সাথে কাজ করা সহজ। কয়েকটি ট্যাগ ঘোষণা করে, আপনি সেকেন্ডের মধ্যে একটি পৃষ্ঠা লিখতে পারেন যাতে উপস্থাপনা এবং কাঠামো উভয়ই রয়েছে। যাইহোক, নিজেই এইচটিএমএল সব দরকারী নয়. মানুষের পক্ষে পাঠ্য-ভিত্তিক ভাষা বোঝা সহজ, কিন্তু মেশিনের আরও কিছু প্রয়োজন। ডকুমেন্ট অবজেক্ট মডেল, বা DOM লিখুন।

যখন ব্রাউজার একটি ওয়েব পৃষ্ঠা লোড করে তখন এটি আকর্ষণীয় জিনিসগুলির একটি গুচ্ছ করে। এটি যে জিনিসগুলি করে তার মধ্যে একটি হল লেখকের HTML কে একটি লাইভ ডকুমেন্টে রূপান্তর করা৷ মূলত, পৃষ্ঠার গঠন বোঝার জন্য, ব্রাউজারটি HTML (টেক্সটের স্ট্যাটিক স্ট্রিং) একটি ডেটা মডেলে (অবজেক্ট/নোড) পার্স করে। ব্রাউজার এই নোডগুলির একটি ট্রি তৈরি করে HTML এর অনুক্রম সংরক্ষণ করে: DOM। DOM সম্পর্কে দুর্দান্ত জিনিস হল এটি আপনার পৃষ্ঠার একটি লাইভ উপস্থাপনা। আমরা যে স্ট্যাটিক এইচটিএমএল লিখি তার বিপরীতে, ব্রাউজার-উত্পাদিত নোডগুলিতে বৈশিষ্ট্য, পদ্ধতি এবং সর্বোত্তম… প্রোগ্রাম দ্বারা ম্যানিপুলেট করা যেতে পারে! তাই আমরা সরাসরি জাভাস্ক্রিপ্ট ব্যবহার করে 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... ছায়ায়

Shadow DOM হল দুটি পার্থক্য সহ সাধারণ DOM: 1) এটি কীভাবে তৈরি/ব্যবহার করা হয় এবং 2) বাকি পৃষ্ঠাগুলির সাথে এটি কীভাবে আচরণ করে। সাধারণত, আপনি DOM নোড তৈরি করেন এবং অন্য উপাদানের সন্তান হিসাবে তাদের যুক্ত করেন। ছায়া 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 বিশেষভাবে কার্যকর। একটি উপাদানের এইচটিএমএল, সিএসএস এবং জেএসকে বিভক্ত করতে ছায়া 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> s, <header> s, <form> s, <input> s) অ্যাপ তৈরি করতে একত্রিত হয়। এই ট্যাগগুলির মধ্যে কিছু এমনকি একে অপরের সাথে কাজ করে। কম্পোজিশন কেন <select> , <details> , <form> , এবং <video> এর মত নেটিভ উপাদানগুলি এত নমনীয়। এই ট্যাগগুলির প্রতিটি শিশু হিসাবে নির্দিষ্ট HTML গ্রহণ করে এবং তাদের সাথে বিশেষ কিছু করে। উদাহরণস্বরূপ, <select> জানে কিভাবে <option> এবং <optgroup> ড্রপডাউন এবং মাল্টি-সিলেক্ট উইজেটে রেন্ডার করতে হয়। <details> উপাদানটি একটি প্রসারণযোগ্য তীর হিসাবে <summary> রেন্ডার করে। এমনকি <video> ও জানে কিভাবে নির্দিষ্ট বাচ্চাদের সাথে মোকাবিলা করতে হয়: <source> উপাদানগুলি রেন্ডার করা হয় না, কিন্তু তারা ভিডিওর আচরণকে প্রভাবিত করে। কি জাদু!

পরিভাষা: হালকা DOM বনাম ছায়া DOM

ছায়া 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>

ছায়া 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> উপাদান

ছায়া DOM <slot> উপাদান ব্যবহার করে একসাথে বিভিন্ন DOM গাছ রচনা করে। স্লটগুলি হল আপনার উপাদানের মধ্যে স্থানধারক যা ব্যবহারকারীরা তাদের নিজস্ব মার্কআপ দিয়ে পূরণ করতে পারে ৷ এক বা একাধিক স্লট সংজ্ঞায়িত করে, আপনি আপনার উপাদানের ছায়া DOM-এ রেন্ডার করার জন্য বাইরের মার্কআপকে আমন্ত্রণ জানান। মূলত, আপনি বলছেন "এখানে ব্যবহারকারীর মার্কআপ রেন্ডার করুন"

উপাদানগুলিকে ছায়া DOM সীমানা "ক্রস" করার অনুমতি দেওয়া হয় যখন একটি <slot> তাদের ভিতরে আমন্ত্রণ জানায়৷ এই উপাদানগুলিকে বিতরণ করা নোড বলা হয়৷ ধারণাগতভাবে, বিতরণ করা নোডগুলি কিছুটা উদ্ভট মনে হতে পারে। স্লট শারীরিকভাবে 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 ব্যবহার করে এমন একটি উপাদান প্রধান পৃষ্ঠা দ্বারা স্টাইল করা যেতে পারে, এর নিজস্ব শৈলী সংজ্ঞায়িত করা যেতে পারে, অথবা ব্যবহারকারীদের ডিফল্ট ওভাররাইড করার জন্য হুক ( সিএসএস কাস্টম বৈশিষ্ট্য আকারে) প্রদান করতে পারে।

উপাদান-সংজ্ঞায়িত শৈলী

ছায়া DOM-এর সবচেয়ে দরকারী বৈশিষ্ট্য হল স্কোপড CSS :

  • বাইরের পৃষ্ঠা থেকে CSS নির্বাচকরা আপনার উপাদানের ভিতরে প্রযোজ্য নয়।
  • ভিতরে সংজ্ঞায়িত শৈলী রক্তপাত না. তারা হোস্ট উপাদান স্কোপ করছি.

ছায়া DOM-এর ভিতরে ব্যবহৃত CSS নির্বাচকরা স্থানীয়ভাবে আপনার উপাদানে প্রযোজ্য । অনুশীলনে, এর অর্থ হল আমরা পৃষ্ঠার অন্য কোথাও দ্বন্দ্বের বিষয়ে চিন্তা না করে আবার সাধারণ আইডি/শ্রেণীর নাম ব্যবহার করতে পারি। সহজ সিএসএস নির্বাচকরা ছায়া DOM-এর মধ্যে একটি সেরা অনুশীলন। তারা পারফরম্যান্সের জন্যও ভাল।

উদাহরণ - শ্যাডো রুটে সংজ্ঞায়িত শৈলী স্থানীয়

#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> s ব্যবহারকারীর হালকা 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 প্রদান করে
  • Event.composedPath() ছায়া DOM-এর ভিতরের উপাদানগুলির সাথে যুক্ত ইভেন্টের জন্য, [] ফেরত দেয়

আপনার কেন কখনই {mode: 'closed'} দিয়ে ওয়েব কম্পোনেন্ট তৈরি করা উচিত নয় তার আমার সারাংশ এখানে দেওয়া হল:

  1. নিরাপত্তার কৃত্রিম অনুভূতি। কোনো আক্রমণকারীকে Element.prototype.attachShadow হাইজ্যাক করা থেকে আটকাতে পারে না।

  2. ক্লোজড মোড আপনার কাস্টম এলিমেন্ট কোডকে এর নিজস্ব ছায়া 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');
        }
        ...
    });
    
  3. ক্লোজড মোড শেষ ব্যবহারকারীদের জন্য আপনার উপাদানকে কম নমনীয় করে তোলে । আপনি যখন ওয়েব উপাদান তৈরি করবেন, তখন এমন একটি সময় আসবে যখন আপনি একটি বৈশিষ্ট্য যোগ করতে ভুলে যাবেন। একটি কনফিগারেশন বিকল্প। একটি ব্যবহার কেস ব্যবহারকারী চায়. একটি সাধারণ উদাহরণ হল অভ্যন্তরীণ নোডগুলির জন্য পর্যাপ্ত স্টাইলিং হুকগুলি অন্তর্ভুক্ত করতে ভুলে যাওয়া৷ বন্ধ মোডের সাথে, ব্যবহারকারীদের ডিফল্ট ও টুইক শৈলী ওভাররাইড করার কোন উপায় নেই। উপাদানটির অভ্যন্তরীণ অ্যাক্সেস করতে সক্ষম হওয়া অত্যন্ত সহায়ক। পরিশেষে, ব্যবহারকারীরা আপনার উপাদানকে কাঁটাচামচ করবে, অন্যটি খুঁজে পাবে বা তাদের নিজস্ব তৈরি করবে যদি এটি তারা যা চায় তা না করে :(

জেএস-এ স্লট নিয়ে কাজ করা

ছায়া DOM API স্লট এবং বিতরণ করা নোডগুলির সাথে কাজ করার জন্য ইউটিলিটি প্রদান করে। একটি কাস্টম উপাদান রচনা করার সময় এগুলি কাজে আসে৷

স্লট পরিবর্তন ইভেন্ট

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 আপনাকে বলে যে কোন কম্পোনেন্ট স্লটে আপনার এলিমেন্ট বরাদ্দ করা হয়েছে।

ছায়া DOM ইভেন্ট মডেল

যখন কোনো ইভেন্ট শ্যাডো DOM থেকে বুদবুদ ওঠে তখন এর টার্গেট এনক্যাপসুলেশন বজায় রাখতে সামঞ্জস্য করা হয় যা ছায়া DOM প্রদান করে। অর্থাৎ, আপনার ছায়া DOM-এর মধ্যে অভ্যন্তরীণ উপাদানগুলির পরিবর্তে ইভেন্টগুলিকে কম্পোনেন্ট থেকে এসেছে বলে মনে করার জন্য পুনরায় টার্গেট করা হয়। কিছু ঘটনা ছায়া DOM এর বাইরেও প্রচার করে না।

যে ঘটনাগুলি ছায়ার সীমানা অতিক্রম করে তা হল:

  • ফোকাস ইভেন্ট: blur , focus , focusin , focusout
  • মাউস ইভেন্ট: click , dblclick , mousedown , mouseenter , mousemove ইত্যাদি।
  • চাকা ঘটনা: wheel
  • ইনপুট ইভেন্ট: beforeinput , input
  • কীবোর্ড ইভেন্ট: keydown , keyup
  • রচনা ইভেন্ট: compositionstart , compositionupdate , compositionend
  • ড্র্যাগ ইভেন্ট: 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: মিথ্যা এবং অভ্যন্তরীণ ইনপুট ফোকাস করা হয়।
delegatesFocus: false এবং অভ্যন্তরীণ <input> ফোকাস করা হয়।
delegatesFocus: মিথ্যা এবং x-ফোকাস     ফোকাস লাভ করে (যেমন এতে ট্যাবিনডেক্স='0' আছে)।
delegatesFocus: false এবং <x-focus> ফোকাস লাভ করে (যেমন এতে tabindex="0" আছে)।
delegatesFocus: false এবং 'Clickable Shadow DOM text' হল     ক্লিক করা হয়েছে (অথবা উপাদানের ছায়া DOM-এর মধ্যে অন্য খালি জায়গাতে ক্লিক করা হয়েছে)।
delegatesFocus: false এবং "ক্লিকযোগ্য ছায়া DOM পাঠ্য" ক্লিক করা হয়েছে (অথবা উপাদানটির ছায়া DOM-এর মধ্যে অন্য খালি এলাকা ক্লিক করা হয়েছে)।

টিপস এবং কৌশল

কয়েক বছর ধরে আমি ওয়েব কম্পোনেন্ট লেখার বিষয়ে একটি বা দুটি জিনিস শিখেছি। আমি মনে করি আপনি এই টিপসগুলির মধ্যে কয়েকটি উপাদান রচনা এবং ছায়া DOM ডিবাগ করার জন্য দরকারী খুঁজে পাবেন।

CSS কন্টেনমেন্ট ব্যবহার করুন

সাধারণত, একটি ওয়েব কম্পোনেন্টের লেআউট/স্টাইল/পেইন্ট মোটামুটি স্বয়ংসম্পূর্ণ। একটি পারফ জয়ের জন্য : :hostCSS কন্টেনমেন্ট ব্যবহার করুন:

<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('*'));

একটি <টেমপ্লেট> থেকে উপাদান তৈরি করা

.innerHTML ব্যবহার করে শ্যাডো রুট তৈরি করার পরিবর্তে, আমরা একটি ঘোষণামূলক <template> ব্যবহার করতে পারি। টেমপ্লেটগুলি একটি ওয়েব উপাদানের গঠন ঘোষণা করার জন্য একটি আদর্শ স্থানধারক।

"কাস্টম উপাদান: পুনঃব্যবহারযোগ্য ওয়েব উপাদান নির্মাণ" এর উদাহরণটি দেখুন।

ইতিহাস এবং ব্রাউজার সমর্থন

আপনি যদি গত কয়েক বছর ধরে ওয়েব কম্পোনেন্ট অনুসরণ করে থাকেন, তাহলে আপনি জানতে পারবেন যে Chrome 35+/Opera কিছু সময়ের জন্য ছায়া DOM-এর একটি পুরানো সংস্করণ পাঠাচ্ছে। ব্লিঙ্ক কিছু সময়ের জন্য সমান্তরালভাবে উভয় সংস্করণ সমর্থন করতে থাকবে। v0 স্পেক একটি শ্যাডো রুট তৈরি করার জন্য একটি ভিন্ন পদ্ধতি প্রদান করেছে ( v1 এর element.attachShadow এর পরিবর্তে element.createShadowRoot )। পুরানো পদ্ধতিতে কল করা v0 শব্দার্থবিদ্যা সহ একটি ছায়া রুট তৈরি করতে থাকে, তাই বিদ্যমান v0 কোড ভাঙবে না।

আপনি যদি পুরানো v0 স্পেকের প্রতি আগ্রহী হন তবে html5rocks নিবন্ধগুলি দেখুন: 1 , 2 , 3ছায়া DOM v0 এবং v1 এর মধ্যে পার্থক্যগুলির একটি দুর্দান্ত তুলনাও রয়েছে।

ব্রাউজার সমর্থন

Shadow DOM v1 Chrome 53 ( স্থিতি ), Opera 40, Safari 10, এবং Firefox 63-এ পাঠানো হয়েছে। এজ বিকাশ শুরু করেছে

শ্যাডো ডম শনাক্ত করার জন্য, attachShadow অস্তিত্ব পরীক্ষা করুন:

const supportsShadowDOMV1 = !!HTMLElement.prototype.attachShadow;

পলিফিল

ব্রাউজার সমর্থন ব্যাপকভাবে উপলব্ধ না হওয়া পর্যন্ত, shadydom এবং shadycss পলিফিলস আপনাকে v1 বৈশিষ্ট্য দেয়। Shady DOM শ্যাডো 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 দেখুন।

উপসংহার

প্রথমবারের মতো, আমাদের কাছে একটি এপিআই আদিম রয়েছে যা সঠিক CSS স্কোপিং, DOM স্কোপিং এবং সত্যিকারের কম্পোজিশন করে। কাস্টম উপাদানগুলির মতো অন্যান্য ওয়েব কম্পোনেন্ট API-এর সাথে মিলিত, ছায়া DOM হ্যাক ছাড়াই বা <iframe> s এর মতো পুরানো লাগেজ ব্যবহার না করে সত্যিকারের এনক্যাপসুলেটেড উপাদান লেখকের একটি উপায় প্রদান করে।

আমাকে ভুল বুঝবেন না। ছায়া DOM অবশ্যই একটি জটিল প্রাণী! কিন্তু এটা শেখার মূল্য একটি পশু. এর সাথে কিছু সময় কাটান। এটা শিখুন এবং প্রশ্ন জিজ্ঞাসা করুন!

আরও পড়া

FAQ

আমি কি আজ Shadow DOM v1 ব্যবহার করতে পারি?

একটি পলিফিল দিয়ে, হ্যাঁ। ব্রাউজার সমর্থন দেখুন।

ছায়া DOM কি নিরাপত্তা বৈশিষ্ট্য প্রদান করে?

ছায়া DOM একটি নিরাপত্তা বৈশিষ্ট্য নয়. এটি CSS স্কোপিং এবং উপাদানে DOM গাছ লুকানোর জন্য একটি হালকা টুল। আপনি যদি একটি সত্য নিরাপত্তা সীমানা চান, একটি <iframe> ব্যবহার করুন।

একটি ওয়েব উপাদান ছায়া DOM ব্যবহার করতে হবে?

না! ছায়া DOM ব্যবহার করে এমন ওয়েব কম্পোনেন্ট তৈরি করতে হবে না। যাইহোক, শ্যাডো ডম ব্যবহার করে এমন কাস্টম উপাদানগুলি লেখার অর্থ হল আপনি CSS স্কোপিং, DOM এনক্যাপসুলেশন এবং কম্পোজিশনের মতো বৈশিষ্ট্যগুলির সুবিধা নিতে পারেন।

খোলা এবং বন্ধ ছায়া শিকড় মধ্যে পার্থক্য কি?

বদ্ধ ছায়া শিকড় দেখুন।