কাস্টম উপাদান v1 - পুনরায় ব্যবহারযোগ্য ওয়েব উপাদান

কাস্টম উপাদানগুলি ওয়েব বিকাশকারীদের নতুন HTML ট্যাগগুলিকে সংজ্ঞায়িত করতে, বিদ্যমানগুলিকে প্রসারিত করতে এবং পুনরায় ব্যবহারযোগ্য ওয়েব উপাদানগুলি তৈরি করতে দেয়৷

কাস্টম এলিমেন্টের সাহায্যে, ওয়েব ডেভেলপাররা নতুন HTML ট্যাগ তৈরি করতে পারে, বিদ্যমান HTML ট্যাগগুলিকে বিফ-আপ করতে পারে, অথবা অন্যান্য ডেভেলপারদের লেখা উপাদানগুলিকে প্রসারিত করতে পারে৷ API হল ওয়েব উপাদানগুলির ভিত্তি। এটি ভ্যানিলা JS/HTML/CSS ছাড়া আর কিছুই ব্যবহার করে পুনরায় ব্যবহারযোগ্য উপাদান তৈরি করার জন্য একটি ওয়েব মান-ভিত্তিক উপায় নিয়ে আসে। ফলাফল হল কম কোড, মডুলার কোড, এবং আমাদের অ্যাপে আরও পুনঃব্যবহার।

ভূমিকা

ব্রাউজার আমাদের ওয়েব অ্যাপ্লিকেশন গঠনের জন্য একটি চমৎকার টুল দেয়। এটাকে HTML বলা হয়। আপনি এটা শুনে থাকতে পারেন! এটি ঘোষণামূলক, বহনযোগ্য, ভাল সমর্থিত এবং এর সাথে কাজ করা সহজ। এইচটিএমএল যতই ভালো হোক, এর শব্দভান্ডার এবং এক্সটেনসিবিলিটি সীমিত। এইচটিএমএল লিভিং স্ট্যান্ডার্ডে সবসময় আপনার মার্কআপের সাথে JS আচরণকে স্বয়ংক্রিয়ভাবে যুক্ত করার উপায় নেই… এখন পর্যন্ত।

কাস্টম উপাদানগুলি হল এইচটিএমএলকে আধুনিকীকরণ, অনুপস্থিত অংশগুলি পূরণ করা এবং আচরণের সাথে কাঠামোকে একত্রিত করার উত্তর। যদি HTML কোনো সমস্যার সমাধান না দেয়, তাহলে আমরা একটি কাস্টম উপাদান তৈরি করতে পারি যা করে। কাস্টম উপাদানগুলি HTML এর সুবিধাগুলি সংরক্ষণ করার সময় ব্রাউজারকে নতুন কৌশল শেখায়

একটি নতুন উপাদান সংজ্ঞায়িত করা

একটি নতুন HTML উপাদান সংজ্ঞায়িত করতে আমাদের জাভাস্ক্রিপ্টের শক্তি প্রয়োজন!

customElements গ্লোবাল একটি কাস্টম উপাদান সংজ্ঞায়িত করতে এবং ব্রাউজারকে একটি নতুন ট্যাগ সম্পর্কে শেখানোর জন্য ব্যবহৃত হয়। আপনি যে ট্যাগ নামটি তৈরি করতে চান এবং একটি জাভাস্ক্রিপ্ট class যা বেস HTMLElement প্রসারিত করে তার সাথে customElements.define() কল করুন।

উদাহরণ - একটি মোবাইল ড্রয়ার প্যানেল সংজ্ঞায়িত করা, <app-drawer> :

class AppDrawer extends HTMLElement {...}
window.customElements.define('app-drawer', AppDrawer);

// Or use an anonymous class if you don't want a named constructor in current scope.
window.customElements.define('app-drawer', class extends HTMLElement {...});

উদাহরণ ব্যবহার:

<app-drawer></app-drawer>

এটা মনে রাখা গুরুত্বপূর্ণ যে একটি কাস্টম উপাদান ব্যবহার করা একটি <div> বা অন্য কোনো উপাদান ব্যবহার করার চেয়ে আলাদা নয়। দৃষ্টান্তগুলি পৃষ্ঠায় ঘোষণা করা যেতে পারে, জাভাস্ক্রিপ্টে গতিশীলভাবে তৈরি করা যেতে পারে, ইভেন্ট শ্রোতাদের সংযুক্ত করা যেতে পারে ইত্যাদি। আরও উদাহরণের জন্য পড়তে থাকুন।

একটি উপাদানের জাভাস্ক্রিপ্ট API সংজ্ঞায়িত করা

একটি কাস্টম উপাদানের কার্যকারিতা একটি ES2015 class ব্যবহার করে সংজ্ঞায়িত করা হয় যা HTMLElement প্রসারিত করে। HTMLElement প্রসারিত করা নিশ্চিত করে যে কাস্টম উপাদানটি সমগ্র DOM API-এর উত্তরাধিকারসূত্রে প্রাপ্ত হয় এবং এর মানে হল যে কোনো বৈশিষ্ট্য/পদ্ধতি যা আপনি ক্লাসে যোগ করেন তা উপাদানটির DOM ইন্টারফেসের অংশ হয়ে যায়। মূলত, আপনার ট্যাগের জন্য একটি সর্বজনীন JavaScript API তৈরি করতে ক্লাসটি ব্যবহার করুন।

উদাহরণ - <app-drawer> এর DOM ইন্টারফেস সংজ্ঞায়িত করা :

class AppDrawer extends HTMLElement {

  // A getter/setter for an open property.
  get open() {
    return this.hasAttribute('open');
  }

  set open(val) {
    // Reflect the value of the open property as an HTML attribute.
    if (val) {
      this.setAttribute('open', '');
    } else {
      this.removeAttribute('open');
    }
    this.toggleDrawer();
  }

  // A getter/setter for a disabled property.
  get disabled() {
    return this.hasAttribute('disabled');
  }

  set disabled(val) {
    // Reflect the value of the disabled property as an HTML attribute.
    if (val) {
      this.setAttribute('disabled', '');
    } else {
      this.removeAttribute('disabled');
    }
  }

  // Can define constructor arguments if you wish.
  constructor() {
    // If you define a constructor, always call super() first!
    // This is specific to CE and required by the spec.
    super();

    // Setup a click listener on <app-drawer> itself.
    this.addEventListener('click', e => {
      // Don't toggle the drawer if it's disabled.
      if (this.disabled) {
        return;
      }
      this.toggleDrawer();
    });
  }

  toggleDrawer() {
    // ...
  }
}

customElements.define('app-drawer', AppDrawer);

এই উদাহরণে, আমরা একটি ড্রয়ার তৈরি করছি যাতে একটি open সম্পত্তি, disabled সম্পত্তি এবং একটি toggleDrawer() পদ্ধতি রয়েছে। এটি HTML বৈশিষ্ট্য হিসাবে বৈশিষ্ট্যগুলিকেও প্রতিফলিত করে

কাস্টম উপাদানগুলির একটি ঝরঝরে বৈশিষ্ট্য হল যে this একটি শ্রেণির সংজ্ঞার ভিতরে DOM উপাদানকে বোঝায় অর্থাৎ ক্লাসের উদাহরণ। আমাদের উদাহরণে, this <app-drawer> বোঝায়। এটি (😉) উপাদানটি কীভাবে একজন click শ্রোতাকে নিজের সাথে সংযুক্ত করতে পারে! এবং আপনি ইভেন্ট শ্রোতাদের মধ্যে সীমাবদ্ধ নন। সম্পূর্ণ DOM API উপাদান কোডের ভিতরে উপলব্ধ। উপাদানের বৈশিষ্ট্যগুলি অ্যাক্সেস করতে this ব্যবহার করুন, এর বাচ্চাদের পরিদর্শন করুন ( this.children ), ক্যোয়ারী নোড ( this.querySelectorAll('.items') ), ইত্যাদি।

কাস্টম উপাদান তৈরির নিয়ম

  1. একটি কাস্টম উপাদানের নামে অবশ্যই একটি ড্যাশ (-) থাকতে হবে । সুতরাং <x-tags> , <my-element> , এবং <my-awesome-app> সব বৈধ নাম, যখন <tabs> এবং <foo_bar> নয়। এই প্রয়োজনীয়তা তাই HTML পার্সার কাস্টম উপাদানগুলিকে নিয়মিত উপাদান থেকে আলাদা করতে পারে৷ এইচটিএমএলে নতুন ট্যাগ যুক্ত হলে এটি ফরওয়ার্ড সামঞ্জস্যতা নিশ্চিত করে।
  2. আপনি একই ট্যাগ একাধিকবার নিবন্ধন করতে পারবেন না। এটি করার চেষ্টা করা একটি DOMException নিক্ষেপ করবে। একবার আপনি ব্রাউজারকে একটি নতুন ট্যাগ সম্পর্কে বলেছেন, এটাই। কোন পিছু নেওয়া.
  3. কাস্টম উপাদানগুলি স্ব-বন্ধ হতে পারে না কারণ HTML শুধুমাত্র কয়েকটি উপাদানকে স্ব-বন্ধ হতে দেয়। সর্বদা একটি ক্লোজিং ট্যাগ লিখুন ( <app-drawer></app-drawer> )।

কাস্টম উপাদান প্রতিক্রিয়া

একটি কাস্টম উপাদান তার অস্তিত্বের আকর্ষণীয় সময়ে কোড চালানোর জন্য বিশেষ জীবনচক্র হুক সংজ্ঞায়িত করতে পারে। এগুলোকে কাস্টম উপাদান বিক্রিয়া বলা হয়।

নাম যখন ডাকল
constructor উপাদানটির একটি উদাহরণ তৈরি বা আপগ্রেড করা হয়। স্টেট আরম্ভ করার জন্য, ইভেন্ট শ্রোতাদের সেট আপ করার জন্য বা ছায়া ডোম তৈরি করার জন্য দরকারী। আপনি constructor কি করতে পারেন তার উপর বিধিনিষেধের জন্য স্পেস দেখুন।
connectedCallback DOM-এ উপাদান ঢোকানোর সময় কল করা হয়। সেটআপ কোড চালানোর জন্য দরকারী, যেমন সম্পদ আনা বা রেন্ডারিং। সাধারণত, আপনার এই সময় পর্যন্ত কাজ বিলম্বিত করার চেষ্টা করা উচিত।
disconnectedCallback DOM থেকে উপাদানটি সরানো হলে প্রতিবার কল করা হয়। ক্লিন আপ কোড চালানোর জন্য দরকারী।
attributeChangedCallback(attrName, oldVal, newVal) যখন একটি পর্যবেক্ষিত বৈশিষ্ট্য যোগ করা, সরানো, আপডেট করা বা প্রতিস্থাপন করা হয় তখন বলা হয়। পার্সার দ্বারা একটি উপাদান তৈরি করা হলে বা আপগ্রেড করা হলে প্রাথমিক মানগুলির জন্যও বলা হয়। দ্রষ্টব্য: শুধুমাত্র observedAttributes বৈশিষ্ট্যে তালিকাভুক্ত বৈশিষ্ট্যগুলি এই কলব্যাকটি পাবে৷
adoptedCallback কাস্টম উপাদানটিকে একটি নতুন document স্থানান্তরিত করা হয়েছে (উদাহরণস্বরূপ, যাকে document.adoptNode(el) বলা হয়)।

প্রতিক্রিয়া কলব্যাকগুলি সিঙ্ক্রোনাস । যদি কেউ আপনার উপাদানে el.setAttribute() কল করে, ব্রাউজার অবিলম্বে attributeChangedCallback() কল করবে। একইভাবে, DOM থেকে আপনার উপাদান সরানোর পর আপনি একটি disconnectedCallback() পাবেন (যেমন ব্যবহারকারী কল করে el.remove() )।

উদাহরণ: <app-drawer> এ কাস্টম উপাদান প্রতিক্রিয়া যোগ করা :

class AppDrawer extends HTMLElement {
  constructor() {
    super(); // always call super() first in the constructor.
    // ...
  }

  connectedCallback() {
    // ...
  }

  disconnectedCallback() {
    // ...
  }

  attributeChangedCallback(attrName, oldVal, newVal) {
    // ...
  }
}

প্রতিক্রিয়া সংজ্ঞায়িত করুন যদি/যখন এটি বোধ হয়। যদি আপনার উপাদান যথেষ্ট জটিল হয় এবং connectedCallback() এ IndexedDB এর সাথে একটি সংযোগ খোলে, disconnectedCallback() এ প্রয়োজনীয় পরিষ্কারের কাজটি করুন। কিন্তু সতর্কতা অবলম্বন করা আবশ্যক! আপনি সব পরিস্থিতিতে DOM থেকে আপনার উপাদান সরানোর উপর নির্ভর করতে পারবেন না। উদাহরণস্বরূপ, ব্যবহারকারী ট্যাবটি বন্ধ করলে disconnectedCallback() কখনই কল করা হবে না।

বৈশিষ্ট্য এবং গুণাবলী

বৈশিষ্ট্য প্রতিফলিত বৈশিষ্ট্য

এইচটিএমএল বৈশিষ্ট্যগুলির জন্য এটি একটি সাধারণ বিষয় যে তাদের মানকে এইচটিএমএল বৈশিষ্ট্য হিসাবে DOM-এ প্রতিফলিত করা হয়। উদাহরণস্বরূপ, যখন JS-এ hidden বা id মান পরিবর্তন করা হয়:

div.id = 'my-id';
div.hidden = true;

মানগুলি লাইভ DOM-এ বৈশিষ্ট্য হিসাবে প্রয়োগ করা হয়:

<div id="my-id" hidden>

একে বলা হয় " বৈশিষ্ট্যের প্রতিফলনকারী বৈশিষ্ট্য "। এইচটিএমএল এর প্রায় প্রতিটি সম্পত্তি এটি করে। কেন? বৈশিষ্ট্যগুলি ঘোষণামূলকভাবে একটি উপাদান কনফিগার করার জন্যও দরকারী এবং কিছু নির্দিষ্ট API যেমন অ্যাক্সেসিবিলিটি এবং CSS নির্বাচকরা কাজ করার জন্য বৈশিষ্ট্যগুলির উপর নির্ভর করে।

আপনি উপাদানটির DOM উপস্থাপনাকে এর JavaScript অবস্থার সাথে সিঙ্কে রাখতে চান এমন যেকোন জায়গায় একটি সম্পত্তি প্রতিফলিত করা কার্যকর। আপনি একটি সম্পত্তি প্রতিফলিত করতে চাইতে পারেন একটি কারণ তাই ব্যবহারকারী-সংজ্ঞায়িত স্টাইলিং প্রযোজ্য যখন JS রাষ্ট্র পরিবর্তন.

আমাদের <app-drawer> স্মরণ করুন। এই উপাদানটির একজন ভোক্তা এটিকে বিবর্ণ করতে এবং/অথবা ব্যবহারকারীর মিথস্ক্রিয়া প্রতিরোধ করতে চাইতে পারে যখন এটি নিষ্ক্রিয় থাকে:

app-drawer[disabled] {
  opacity: 0.5;
  pointer-events: none;
}

যখন disabled প্রপার্টি জেএস-এ পরিবর্তন করা হয়, তখন আমরা চাই যে সেই অ্যাট্রিবিউটটি DOM-এ যোগ করা হোক যাতে ব্যবহারকারীর নির্বাচক মেলে। উপাদানটি একই নামের একটি বৈশিষ্ট্যের মান প্রতিফলিত করে সেই আচরণটি প্রদান করতে পারে:

get disabled() {
  return this.hasAttribute('disabled');
}

set disabled(val) {
  // Reflect the value of `disabled` as an attribute.
  if (val) {
    this.setAttribute('disabled', '');
  } else {
    this.removeAttribute('disabled');
  }
  this.toggleDrawer();
}

গুণাবলী পরিবর্তন পর্যবেক্ষণ

এইচটিএমএল অ্যাট্রিবিউট ব্যবহারকারীদের জন্য প্রাথমিক অবস্থা ঘোষণা করার একটি সুবিধাজনক উপায়:

<app-drawer open disabled></app-drawer>

এলিমেন্ট একটি attributeChangedCallback সংজ্ঞায়িত করে অ্যাট্রিবিউট পরিবর্তনের প্রতিক্রিয়া জানাতে পারে। ব্রাউজার observedAttributes অ্যারে তালিকাভুক্ত বৈশিষ্ট্যগুলিতে প্রতিটি পরিবর্তনের জন্য এই পদ্ধতিটিকে কল করবে।

class AppDrawer extends HTMLElement {
  // ...

  static get observedAttributes() {
    return ['disabled', 'open'];
  }

  get disabled() {
    return this.hasAttribute('disabled');
  }

  set disabled(val) {
    if (val) {
      this.setAttribute('disabled', '');
    } else {
      this.removeAttribute('disabled');
    }
  }

  // Only called for the disabled and open attributes due to observedAttributes
  attributeChangedCallback(name, oldValue, newValue) {
    // When the drawer is disabled, update keyboard/screen reader behavior.
    if (this.disabled) {
      this.setAttribute('tabindex', '-1');
      this.setAttribute('aria-disabled', 'true');
    } else {
      this.setAttribute('tabindex', '0');
      this.setAttribute('aria-disabled', 'false');
    }
    // TODO: also react to the open attribute changing.
  }
}

উদাহরণে, যখন একটি disabled বৈশিষ্ট্য পরিবর্তন করা হয় তখন আমরা <app-drawer> এ অতিরিক্ত বৈশিষ্ট্য সেট করছি। যদিও আমরা এটি এখানে করছি না, আপনি একটি JS প্রপার্টি এর অ্যাট্রিবিউটের সাথে সিঙ্ক রাখতে attributeChangedCallback ব্যবহার করতে পারেন।

উপাদান আপগ্রেড

ক্রমান্বয়ে উন্নত HTML

আমরা ইতিমধ্যেই শিখেছি যে customElements.define() কল করে কাস্টম উপাদানগুলিকে সংজ্ঞায়িত করা হয়। কিন্তু এর মানে এই নয় যে আপনাকে একযোগে একটি কাস্টম উপাদানকে সংজ্ঞায়িত + নিবন্ধন করতে হবে।

কাস্টম উপাদানগুলি তাদের সংজ্ঞা নিবন্ধিত হওয়ার আগে ব্যবহার করা যেতে পারে

প্রগতিশীল বর্ধন কাস্টম উপাদানগুলির একটি বৈশিষ্ট্য। অন্য কথায়, আপনি পৃষ্ঠায় <app-drawer> উপাদানগুলির একটি গুচ্ছ ঘোষণা করতে পারেন এবং অনেক পরে পর্যন্ত customElements.define('app-drawer', ...) ব্যবহার করবেন না। এর কারণ হল ব্রাউজার সম্ভাব্য কাস্টম উপাদানগুলিকে অজানা ট্যাগের জন্য ভিন্নভাবে ব্যবহার করে। define() কল করার এবং একটি বিদ্যমান এলিমেন্টকে ক্লাস ডেফিনেশন সহ অনুমোদন করার প্রক্রিয়াকে "এলিমেন্ট আপগ্রেড" বলা হয়।

একটি ট্যাগ নাম কখন সংজ্ঞায়িত হবে তা জানতে, আপনি window.customElements.whenDefined() ব্যবহার করতে পারেন। এটি একটি প্রতিশ্রুতি প্রদান করে যা সমাধান করে যখন উপাদানটি সংজ্ঞায়িত হয়।

customElements.whenDefined('app-drawer').then(() => {
  console.log('app-drawer defined');
});

উদাহরণ - চাইল্ড উপাদানের একটি সেট আপগ্রেড না হওয়া পর্যন্ত কাজ বিলম্বিত করুন

<share-buttons>
  <social-button type="twitter"><a href="...">Twitter</a></social-button>
  <social-button type="fb"><a href="...">Facebook</a></social-button>
  <social-button type="plus"><a href="...">G+</a></social-button>
</share-buttons>
// Fetch all the children of <share-buttons> that are not defined yet.
let undefinedButtons = buttons.querySelectorAll(':not(:defined)');

let promises = [...undefinedButtons].map((socialButton) => {
  return customElements.whenDefined(socialButton.localName);
});

// Wait for all the social-buttons to be upgraded.
Promise.all(promises).then(() => {
  // All social-button children are ready.
});

উপাদান-সংজ্ঞায়িত বিষয়বস্তু

কাস্টম উপাদানগুলি উপাদান কোডের ভিতরে DOM API ব্যবহার করে তাদের নিজস্ব সামগ্রী পরিচালনা করতে পারে। প্রতিক্রিয়া এই জন্য কাজে আসে.

উদাহরণ - কিছু ডিফল্ট HTML দিয়ে একটি উপাদান তৈরি করুন:

customElements.define('x-foo-with-markup', class extends HTMLElement {
  connectedCallback() {
    this.innerHTML = "<b>I'm an x-foo-with-markup!</b>";
  }
  // ...
});

এই ট্যাগ ঘোষণা করা হবে:

<x-foo-with-markup>
  <b>I'm an x-foo-with-markup!</b>
</x-foo-with-markup>

// TODO: DevSite - কোড নমুনা সরানো হয়েছে কারণ এটি ইনলাইন ইভেন্ট হ্যান্ডলার ব্যবহার করেছে

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

Shadow DOM একটি উপাদানের মালিকানা, রেন্ডার এবং স্টাইল করার একটি উপায় প্রদান করে DOM এর একটি অংশ যা বাকি পৃষ্ঠা থেকে আলাদা। হেক, আপনি এমনকি একটি ট্যাগের মধ্যে একটি সম্পূর্ণ অ্যাপ লুকিয়ে রাখতে পারেন:

<!-- chat-app's implementation details are hidden away in Shadow DOM. -->
<chat-app></chat-app>

একটি কাস্টম উপাদানে Shadow DOM ব্যবহার করতে, আপনার constructor ভিতরে this.attachShadow কল করুন:

let tmpl = document.createElement('template');
tmpl.innerHTML = `
  <style>:host { ... }</style> <!-- look ma, scoped styles -->
  <b>I'm in shadow dom!</b>
  <slot></slot>
`;

customElements.define('x-foo-shadowdom', class extends HTMLElement {
  constructor() {
    super(); // always call super() first in the constructor.

    // Attach a shadow root to the element.
    let shadowRoot = this.attachShadow({mode: 'open'});
    shadowRoot.appendChild(tmpl.content.cloneNode(true));
  }
  // ...
});

উদাহরণ ব্যবহার:

<x-foo-shadowdom>
  <p><b>User's</b> custom text</p>
</x-foo-shadowdom>

<!-- renders as -->
<x-foo-shadowdom>
  #shadow-root
  <b>I'm in shadow dom!</b>
  <slot></slot> <!-- slotted content appears here -->
</x-foo-shadowdom>

ব্যবহারকারীর কাস্টম পাঠ্য

// TODO: DevSite - কোড নমুনা সরানো হয়েছে কারণ এটি ইনলাইন ইভেন্ট হ্যান্ডলার ব্যবহার করেছে

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

যারা অপরিচিত তাদের জন্য, <template> উপাদানটি আপনাকে DOM-এর অংশগুলি ঘোষণা করতে দেয় যা পার্স করা হয়, পৃষ্ঠা লোডের সময় নিষ্ক্রিয় হয় এবং রানটাইমে পরে সক্রিয় করা যেতে পারে। এটি ওয়েব উপাদান পরিবারে আদিম আরেকটি API। টেমপ্লেটগুলি একটি কাস্টম উপাদানের গঠন ঘোষণা করার জন্য একটি আদর্শ স্থানধারক

উদাহরণ: একটি <template> থেকে তৈরি ছায়া DOM সামগ্রীর সাথে একটি উপাদান নিবন্ধন করা:

<template id="x-foo-from-template">
  <style>
    p { color: green; }
  </style>
  <p>I'm in Shadow DOM. My markup was stamped from a &lt;template&gt;.</p>
</template>

<script>
  let tmpl = document.querySelector('#x-foo-from-template');
  // If your code is inside of an HTML Import you'll need to change the above line to:
  // let tmpl = document.currentScript.ownerDocument.querySelector('#x-foo-from-template');

  customElements.define('x-foo-from-template', class extends HTMLElement {
    constructor() {
      super(); // always call super() first in the constructor.
      let shadowRoot = this.attachShadow({mode: 'open'});
      shadowRoot.appendChild(tmpl.content.cloneNode(true));
    }
    // ...
  });
</script>

কোড এই কয়েক লাইন একটি মুষ্ট্যাঘাত প্যাক. চলুন জেনে নেওয়া যাক কী কী কাজ চলছে:

  1. আমরা HTML এ একটি নতুন উপাদান সংজ্ঞায়িত করছি: <x-foo-from-template>
  2. উপাদানটির ছায়া DOM একটি <template> থেকে তৈরি করা হয়
  3. এলিমেন্টের DOM হল এলিমেন্টের জন্য স্থানীয় ধন্যবাদ Shadow DOM-এর জন্য
  4. এলিমেন্টের অভ্যন্তরীণ CSS এলিমেন্টের জন্য স্কোপ করা হয়েছে শ্যাডো ডমকে ধন্যবাদ

// TODO: DevSite - কোড নমুনা সরানো হয়েছে কারণ এটি ইনলাইন ইভেন্ট হ্যান্ডলার ব্যবহার করেছে

একটি কাস্টম উপাদান স্টাইলিং

এমনকি যদি আপনার উপাদান শ্যাডো DOM ব্যবহার করে তার নিজস্ব স্টাইলিং সংজ্ঞায়িত করে, ব্যবহারকারীরা তাদের পৃষ্ঠা থেকে আপনার কাস্টম উপাদানকে স্টাইল করতে পারে। এগুলোকে "ব্যবহারকারী-সংজ্ঞায়িত শৈলী" বলা হয়।

<!-- user-defined styling -->
<style>
  app-drawer {
    display: flex;
  }
  panel-item {
    transition: opacity 400ms ease-in-out;
    opacity: 0.3;
    flex: 1;
    text-align: center;
    border-radius: 50%;
  }
  panel-item:hover {
    opacity: 1.0;
    background: rgb(255, 0, 255);
    color: white;
  }
  app-panel > panel-item {
    padding: 5px;
    list-style: none;
    margin: 0 7px;
  }
</style>

<app-drawer>
  <panel-item>Do</panel-item>
  <panel-item>Re</panel-item>
  <panel-item>Mi</panel-item>
</app-drawer>

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

প্রাক-স্টাইল অনিবন্ধিত উপাদান

কোনো এলিমেন্ট আপগ্রেড করার আগে আপনি এটিকে সিএসএসে টার্গেট করতে পারেন :defined pseudo-class ব্যবহার করে। এটি একটি উপাদান প্রাক-স্টাইল করার জন্য দরকারী। উদাহরণস্বরূপ, আপনি অনির্ধারিত উপাদানগুলি লুকিয়ে এবং সংজ্ঞায়িত হয়ে গেলে সেগুলিকে বিবর্ণ করে লেআউট বা অন্যান্য ভিজ্যুয়াল FOUC প্রতিরোধ করতে চাইতে পারেন।

উদাহরণ - <app-drawer> সংজ্ঞায়িত হওয়ার আগে লুকান:

app-drawer:not(:defined) {
  /* Pre-style, give layout, replicate app-drawer's eventual styles, etc. */
  display: inline-block;
  height: 100vh;
  opacity: 0;
  transition: opacity 0.3s ease-in-out;
}

<app-drawer> সংজ্ঞায়িত হওয়ার পরে, নির্বাচক ( app-drawer:not(:defined) ) আর মেলে না।

প্রসারিত উপাদান

কাস্টম এলিমেন্টস এপিআই নতুন এইচটিএমএল এলিমেন্ট তৈরির জন্য উপযোগী, তবে এটি অন্যান্য কাস্টম উপাদান বা এমনকি ব্রাউজারের অন্তর্নির্মিত এইচটিএমএল প্রসারিত করার জন্যও কার্যকর।

একটি কাস্টম উপাদান প্রসারিত

অন্য কাস্টম উপাদান প্রসারিত করা হয় তার শ্রেণী সংজ্ঞা প্রসারিত করে।

উদাহরণ - <fancy-app-drawer> তৈরি করুন যা <app-drawer> প্রসারিত করে :

class FancyDrawer extends AppDrawer {
  constructor() {
    super(); // always call super() first in the constructor. This also calls the extended class' constructor.
    // ...
  }

  toggleDrawer() {
    // Possibly different toggle implementation?
    // Use ES2015 if you need to call the parent method.
    // super.toggleDrawer()
  }

  anotherMethod() {
    // ...
  }
}

customElements.define('fancy-app-drawer', FancyDrawer);

নেটিভ এইচটিএমএল উপাদান প্রসারিত করা

ধরা যাক আপনি একটি শৌখিন <button> তৈরি করতে চেয়েছিলেন। <button> এর আচরণ এবং কার্যকারিতা প্রতিলিপি করার পরিবর্তে, একটি ভাল বিকল্প হল কাস্টম উপাদানগুলি ব্যবহার করে বিদ্যমান উপাদানটিকে ক্রমান্বয়ে উন্নত করা।

একটি কাস্টমাইজড বিল্ট-ইন উপাদান হল একটি কাস্টম উপাদান যা ব্রাউজারের অন্তর্নির্মিত HTML ট্যাগগুলির একটিকে প্রসারিত করে। একটি বিদ্যমান উপাদান প্রসারিত করার প্রাথমিক সুবিধা হল এর সমস্ত বৈশিষ্ট্য (DOM বৈশিষ্ট্য, পদ্ধতি, অ্যাক্সেসযোগ্যতা) অর্জন করা। একটি প্রগতিশীল ওয়েব অ্যাপ লেখার জন্য বিদ্যমান এইচটিএমএল উপাদানগুলিকে ক্রমবর্ধমানভাবে উন্নত করার চেয়ে ভাল উপায় আর নেই৷

একটি উপাদান প্রসারিত করার জন্য, আপনাকে একটি শ্রেণী সংজ্ঞা তৈরি করতে হবে যা সঠিক DOM ইন্টারফেস থেকে উত্তরাধিকারসূত্রে পাওয়া যায়। উদাহরণস্বরূপ, একটি কাস্টম উপাদান যা <button> প্রসারিত করে HTMLElement পরিবর্তে HTMLButtonElement থেকে উত্তরাধিকারী হতে হবে। একইভাবে, <img> প্রসারিত একটি উপাদানকে HTMLImageElement প্রসারিত করতে হবে।

উদাহরণ - প্রসারিত <button> :

// See https://html.spec.whatwg.org/multipage/indices.html#element-interfaces
// for the list of other DOM interfaces.
class FancyButton extends HTMLButtonElement {
  constructor() {
    super(); // always call super() first in the constructor.
    this.addEventListener('click', e => this.drawRipple(e.offsetX, e.offsetY));
  }

  // Material design ripple animation.
  drawRipple(x, y) {
    let div = document.createElement('div');
    div.classList.add('ripple');
    this.appendChild(div);
    div.style.top = `${y - div.clientHeight/2}px`;
    div.style.left = `${x - div.clientWidth/2}px`;
    div.style.backgroundColor = 'currentColor';
    div.classList.add('run');
    div.addEventListener('transitionend', (e) => div.remove());
  }
}

customElements.define('fancy-button', FancyButton, {extends: 'button'});

লক্ষ্য করুন যে একটি নেটিভ এলিমেন্ট প্রসারিত করার সময় define() করার কলটি সামান্য পরিবর্তিত হয়। প্রয়োজনীয় তৃতীয় প্যারামিটার ব্রাউজারকে বলে যে আপনি কোন ট্যাগটি প্রসারিত করছেন। এটি প্রয়োজনীয় কারণ অনেক HTML ট্যাগ একই DOM ইন্টারফেস ভাগ করে। <section> , <address> , এবং <em> (অন্যদের মধ্যে) সবাই HTMLElement শেয়ার করে; <q> এবং <blockquote> উভয়ই HTMLQuoteElement শেয়ার করে; ইত্যাদি... {extends: 'blockquote'} নির্দিষ্ট করা ব্রাউজারকে জানতে দেয় যে আপনি <q> এর পরিবর্তে একটি স্যুপ-আপ <blockquote> তৈরি করছেন। HTML এর DOM ইন্টারফেসের সম্পূর্ণ তালিকার জন্য HTML স্পেক দেখুন।

একটি কাস্টমাইজড বিল্ট-ইন উপাদানের গ্রাহকরা এটি বিভিন্ন উপায়ে ব্যবহার করতে পারেন। তারা নেটিভ ট্যাগে is="" অ্যাট্রিবিউট যোগ করে এটি ঘোষণা করতে পারে:

<!-- This <button> is a fancy button. -->
<button is="fancy-button" disabled>Fancy button!</button>

জাভাস্ক্রিপ্টে একটি উদাহরণ তৈরি করুন:

// Custom elements overload createElement() to support the is="" attribute.
let button = document.createElement('button', {is: 'fancy-button'});
button.textContent = 'Fancy button!';
button.disabled = true;
document.body.appendChild(button);

অথবা new অপারেটর ব্যবহার করুন:

let button = new FancyButton();
button.textContent = 'Fancy button!';
button.disabled = true;

এখানে আরেকটি উদাহরণ রয়েছে যা <img> প্রসারিত করে।

উদাহরণ - প্রসারিত <img> :

customElements.define('bigger-img', class extends Image {
  // Give img default size if users don't specify.
  constructor(width=50, height=50) {
    super(width * 10, height * 10);
  }
}, {extends: 'img'});

ব্যবহারকারীরা এই উপাদানটিকে ঘোষণা করে:

<!-- This <img> is a bigger img. -->
<img is="bigger-img" width="15" height="20">

অথবা জাভাস্ক্রিপ্টে একটি উদাহরণ তৈরি করুন:

const BiggerImage = customElements.get('bigger-img');
const image = new BiggerImage(15, 20); // pass constructor values like so.
console.assert(image.width === 150);
console.assert(image.height === 200);

বিবিধ বিবরণ

অজানা উপাদান বনাম অনির্ধারিত কাস্টম উপাদান

HTML এর সাথে কাজ করার জন্য নমনীয় এবং নমনীয়। উদাহরণস্বরূপ, একটি পৃষ্ঠায় <randomtagthatdoesntexist> ঘোষণা করুন এবং ব্রাউজার এটি গ্রহণ করে পুরোপুরি খুশি। কেন অ-মানক ট্যাগ কাজ করে? উত্তর হল এইচটিএমএল স্পেসিফিকেশন এটির অনুমতি দেয়। যে উপাদানগুলি স্পেসিফিকেশন দ্বারা সংজ্ঞায়িত করা হয় না সেগুলিকে HTMLUnknownElement হিসাবে পার্স করা হয়।

একই কাস্টম উপাদানের জন্য সত্য নয়. সম্ভাব্য কাস্টম উপাদানগুলিকে একটি HTMLElement হিসাবে পার্স করা হয় যদি সেগুলি একটি বৈধ নাম দিয়ে তৈরি করা হয় (একটি "-" সহ)৷ আপনি কাস্টম উপাদান সমর্থন করে এমন একটি ব্রাউজারে এটি পরীক্ষা করতে পারেন। কনসোল ফায়ার আপ করুন: Ctrl + Shift + J (বা Cmd + Opt + J Mac এ) এবং কোডের নিম্নলিখিত লাইনগুলিতে পেস্ট করুন:

// "tabs" is not a valid custom element name
document.createElement('tabs') instanceof HTMLUnknownElement === true

// "x-tabs" is a valid custom element name
document.createElement('x-tabs') instanceof HTMLElement === true

API রেফারেন্স

customElements গ্লোবাল কাস্টম উপাদানগুলির সাথে কাজ করার জন্য দরকারী পদ্ধতিগুলি সংজ্ঞায়িত করে।

define(tagName, constructor, options)

ব্রাউজারে একটি নতুন কাস্টম উপাদান সংজ্ঞায়িত করে।

উদাহরণ

customElements.define('my-app', class extends HTMLElement { ... });
customElements.define(
    'fancy-button', class extends HTMLButtonElement { ... }, {extends: 'button'});

get(tagName)

একটি বৈধ কাস্টম উপাদান ট্যাগ নাম দেওয়া, উপাদানের কন্সট্রাক্টর প্রদান করে। কোনো উপাদান সংজ্ঞা নিবন্ধিত না থাকলে undefined প্রদান করে।

উদাহরণ

let Drawer = customElements.get('app-drawer');
let drawer = new Drawer();

whenDefined(tagName)

একটি প্রতিশ্রুতি প্রদান করে যা কাস্টম উপাদান সংজ্ঞায়িত করা হলে সমাধান করে। যদি উপাদানটি ইতিমধ্যে সংজ্ঞায়িত করা হয়, অবিলম্বে সমাধান করুন। ট্যাগ নাম একটি বৈধ কাস্টম উপাদান নাম না হলে প্রত্যাখ্যান করে।

উদাহরণ

customElements.whenDefined('app-drawer').then(() => {
  console.log('ready!');
});

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

আপনি যদি গত কয়েক বছর ধরে ওয়েব উপাদানগুলি অনুসরণ করে থাকেন, তাহলে আপনি জানতে পারবেন যে Chrome 36+ CustomElements.define document.registerElement() customElements.define() ) ব্যবহার করে Custom Elements API-এর একটি সংস্করণ প্রয়োগ করেছে। এটি এখন মানকটির একটি অবচ্যুত সংস্করণ হিসাবে বিবেচিত হয়, যাকে বলা হয় v0। customElements.define() হল নতুন উষ্ণতা এবং ব্রাউজার বিক্রেতারা কি বাস্তবায়ন করতে শুরু করছে। একে কাস্টম এলিমেন্টস v1 বলা হয়।

আপনি যদি পুরানো v0 স্পেকের প্রতি আগ্রহী হন তবে html5rocks নিবন্ধটি দেখুন।

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

Chrome 54 ( স্থিতি ), Safari 10.1 ( স্থিতি ), এবং Firefox 63 ( স্থিতি ) কাস্টম উপাদান v1 আছে। এজ উন্নয়ন শুরু করেছে

কাস্টম উপাদান শনাক্ত করার জন্য, window.customElements এর অস্তিত্ব পরীক্ষা করুন:

const supportsCustomElementsV1 = 'customElements' in window;

পলিফিল

ব্রাউজার সমর্থন ব্যাপকভাবে উপলব্ধ না হওয়া পর্যন্ত, কাস্টম উপাদান v1 এর জন্য একটি স্বতন্ত্র পলিফিল উপলব্ধ রয়েছে। যাইহোক, আমরা ওয়েব কম্পোনেন্ট পলিফিলগুলিকে সর্বোত্তমভাবে লোড করতে webcomponents.js লোডার ব্যবহার করার পরামর্শ দিই। লোডারটি ব্রাউজার দ্বারা প্রয়োজনীয় শুধুমাত্র প্রয়োজনীয় পলিফিলগুলিকে অ্যাসিঙ্ক্রোনাসভাবে লোড করতে বৈশিষ্ট্য সনাক্তকরণ ব্যবহার করে।

এটি ইনস্টল করুন:

npm install --save @webcomponents/webcomponentsjs

ব্যবহার:

<!-- Use the custom element on the page. -->
<my-element></my-element>

<!-- Load polyfills; note that "loader" will load these async -->
<script src="node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js" defer></script>

<!-- Load a custom element definitions in `waitFor` and return a promise -->
<script type="module">
  function loadScript(src) {
    return new Promise(function(resolve, reject) {
      const script = document.createElement('script');
      script.src = src;
      script.onload = resolve;
      script.onerror = reject;
      document.head.appendChild(script);
    });
  }

  WebComponents.waitFor(() => {
    // At this point we are guaranteed that all required polyfills have
    // loaded, and can use web components APIs.
    // Next, load element definitions that call `customElements.define`.
    // Note: returning a promise causes the custom elements
    // polyfill to wait until all definitions are loaded and then upgrade
    // the document in one batch, for better performance.
    return loadScript('my-element.js');
  });
</script>

উপসংহার

কাস্টম উপাদান আমাদের ব্রাউজারে নতুন HTML ট্যাগ সংজ্ঞায়িত করার জন্য এবং পুনরায় ব্যবহারযোগ্য উপাদান তৈরি করার জন্য একটি নতুন টুল দেয়। শ্যাডো ডম এবং <template> এর মতো অন্যান্য নতুন প্ল্যাটফর্মের সাথে এগুলিকে একত্রিত করুন, এবং আমরা ওয়েব উপাদানগুলির দুর্দান্ত চিত্র উপলব্ধি করতে শুরু করি:

  • ক্রস-ব্রাউজার (ওয়েব স্ট্যান্ডার্ড) পুনর্ব্যবহারযোগ্য উপাদান তৈরি এবং প্রসারিত করার জন্য।
  • শুরু করার জন্য কোন লাইব্রেরি বা ফ্রেমওয়ার্কের প্রয়োজন নেই। ভ্যানিলা JS/HTML FTW!
  • একটি পরিচিত প্রোগ্রামিং মডেল প্রদান করে। এটা শুধু DOM/CSS/HTML।
  • অন্যান্য নতুন ওয়েব প্ল্যাটফর্ম বৈশিষ্ট্যগুলির সাথে ভাল কাজ করে (শ্যাডো ডম, <template> , সিএসএস কাস্টম বৈশিষ্ট্য, ইত্যাদি)
  • ব্রাউজারের DevTools সাথে শক্তভাবে একত্রিত।
  • বিদ্যমান অ্যাক্সেসিবিলিটি বৈশিষ্ট্যগুলি ব্যবহার করুন।