الگو، شکاف و سایه

مزیت اجزای وب قابلیت استفاده مجدد آنهاست: می توانید یک ویجت رابط کاربری ایجاد کنید و چندین بار از آن استفاده مجدد کنید. در حالی که برای ایجاد اجزای وب به جاوا اسکریپت نیاز دارید، نیازی به کتابخانه جاوا اسکریپت ندارید. HTML و API های مرتبط هر آنچه را که نیاز دارید فراهم می کند.

استاندارد Web Component از سه بخش - قالب‌های HTML ، عناصر سفارشی و Shadow DOM تشکیل شده است. ترکیب آنها، ساختن عناصر سفارشی، مستقل (کاپسوله شده)، قابل استفاده مجدد را که می توانند به طور یکپارچه در برنامه های موجود ادغام شوند، مانند سایر عناصر HTML که قبلاً پوشش داده ایم، امکان پذیر می کنند.

در این بخش، عنصر <star-rating> را ایجاد می کنیم، یک مؤلفه وب که به کاربران اجازه می دهد به یک تجربه در مقیاس یک تا پنج ستاره امتیاز دهند. هنگام نامگذاری یک عنصر سفارشی، توصیه می شود از تمام حروف کوچک استفاده کنید. همچنین، یک خط تیره اضافه کنید، زیرا به تمایز بین عناصر معمولی و سفارشی کمک می کند.

ما در مورد استفاده از عناصر <template> و <slot> ، ویژگی slot و جاوا اسکریپت برای ایجاد یک الگو با یک Shadow DOM کپسوله شده بحث خواهیم کرد. سپس از عنصر تعریف شده مجدداً استفاده می کنیم و بخشی از متن را سفارشی می کنیم، درست مانند هر عنصر یا مؤلفه وب. همچنین به طور خلاصه در مورد استفاده از CSS از داخل و خارج از عنصر سفارشی صحبت خواهیم کرد.

عنصر <template>

عنصر <template> برای اعلان قطعاتی از HTML برای شبیه سازی و درج در DOM با جاوا اسکریپت استفاده می شود. محتویات عنصر به طور پیش فرض ارائه نمی شود. بلکه با استفاده از جاوا اسکریپت نمونه سازی می شوند.

<template id="star-rating-template">
  <form>
    <fieldset>
      <legend>Rate your experience:</legend>
      <rating>
        <input type="radio" name="rating" value="1" aria-label="1 star" required />
        <input type="radio" name="rating" value="2" aria-label="2 stars" />
        <input type="radio" name="rating" value="3" aria-label="3 stars" />
        <input type="radio" name="rating" value="4" aria-label="4 stars" />
        <input type="radio" name="rating" value="5" aria-label="5 stars" />
      </rating>
    </fieldset>
    <button type="reset">Reset</button>
    <button type="submit">Submit</button>
  </form>
</template>

از آنجایی که محتویات یک عنصر <template> روی صفحه نمایش داده نمی شود، <form> و محتویات آن رندر نمی شوند. بله، این Codepen خالی است، اما اگر برگه HTML را بررسی کنید، نشانه‌گذاری <template> را خواهید دید.

در این مثال، <form> فرزند یک <template> در DOM نیست. در عوض، محتویات عناصر <template> فرزندان یک DocumentFragment هستند که توسط ویژگی HTMLTemplateElement.content برگردانده شده است. برای قابل مشاهده شدن، جاوا اسکریپت باید برای گرفتن محتویات و الحاق آن محتویات به DOM استفاده شود.

این جاوا اسکریپت مختصر یک عنصر سفارشی ایجاد نکرد. بلکه این مثال محتویات <template> را به <body> اضافه کرده است. محتوا به بخشی از DOM قابل مشاهده و سبک تبدیل شده است.

تصویری از کدپن قبلی همانطور که در DOM نشان داده شده است.

نیاز به جاوا اسکریپت برای پیاده‌سازی یک الگو برای رتبه‌بندی فقط یک ستاره خیلی مفید نیست، اما ایجاد یک مؤلفه وب برای یک ویجت رتبه‌بندی ستاره که بارها استفاده می‌شود و قابل تنظیم است مفید است.

عنصر <slot>

ما یک شکاف برای اضافه کردن یک افسانه سفارشی در هر وقوع اضافه می کنیم. HTML یک عنصر <slot> را به عنوان یک مکان نگهدار در داخل <template> فراهم می کند که اگر نامی ارائه شود، یک "slot با نام" ایجاد می کند. یک اسلات با نام می‌تواند برای سفارشی‌سازی محتوا در یک مؤلفه وب استفاده شود. عنصر <slot> راهی به ما می دهد تا کنترل کنیم فرزندان یک عنصر سفارشی باید در درخت سایه آن درج شوند.

در قالب خود، <legend> به <slot> تغییر می دهیم:

<template id="star-rating-template">
  <form>
    <fieldset>
      <slot name="star-rating-legend">
        <legend>Rate your experience:</legend>
      </slot>

اگر عنصر دارای ویژگی اسلات باشد که مقدار آن با نام شکاف نامگذاری شده مطابقت داشته باشد، از ویژگی name برای تخصیص اسلات به عناصر دیگر استفاده می شود. اگر عنصر سفارشی برای یک اسلات مطابقت نداشته باشد، محتویات <slot> ارائه می شود. بنابراین ما یک <legend> با محتوای عمومی اضافه کردیم که اگر کسی به سادگی <star-rating></star-rating> بدون محتوا در HTML خود وارد کند، قابل ارائه است.

<star-rating>
  <legend slot="star-rating-legend">Blendan Smooth</legend>
</star-rating>
<star-rating>
  <legend slot="star-rating-legend">Hoover Sukhdeep</legend>
</star-rating>
<star-rating>
  <legend slot="star-rating-legend">Toasty McToastface</legend>
  <p>Is this text visible?</p>
</star-rating>

ویژگی slot یک ویژگی جهانی است که برای جایگزینی محتوای <slot> در یک <template> استفاده می شود. در عنصر سفارشی ما، عنصر با ویژگی slot یک <legend> است. نیازی نیست که باشد. در الگوی ما، <slot name="star-rating-legend"> با <anyElement slot="star-rating-legend"> جایگزین می‌شود، جایی که <anyElement> می‌تواند هر عنصری باشد، حتی یک عنصر سفارشی دیگر.

عناصر تعریف نشده

در <template> خود از عنصر <rating> استفاده کردیم. این یک عنصر سفارشی نیست. بلکه یک عنصر ناشناخته است. مرورگرها وقتی عنصری را تشخیص نمی دهند از کار نمی افتند. عناصر ناشناخته HTML توسط مرورگر به عنوان عناصر درون خطی ناشناس تلقی می شوند که می توانند با CSS استایل دهی شوند. مشابه <span> ، عناصر <rating> و <star-rating> هیچ سبک یا معنایی کاربردی-عامل کاربر ندارند.

توجه داشته باشید که <template> و محتویات رندر نمی شوند. <template> یک عنصر شناخته شده است که حاوی محتوایی است که نباید ارائه شود. عنصر <star-rating> هنوز تعریف نشده است. تا زمانی که یک عنصر را تعریف نکنیم، مرورگر آن را مانند تمام عناصر ناشناخته نمایش می دهد. در حال حاضر، <star-rating> شناسایی نشده به عنوان یک عنصر درون خطی ناشناس در نظر گرفته می شود، بنابراین محتوای شامل افسانه ها و <p> در <star-rating> سوم همانطور که اگر در <span> بودند نمایش داده می شوند. در عوض

بیایید عنصر خود را برای تبدیل این عنصر ناشناخته به یک عنصر سفارشی تعریف کنیم.

عناصر سفارشی

جاوا اسکریپت برای تعریف عناصر سفارشی مورد نیاز است. وقتی تعریف شد، محتویات عنصر <star-rating> با یک ریشه سایه حاوی تمام محتویات قالبی که ما با آن مرتبط می کنیم جایگزین می شود. عناصر <slot> از الگو با محتویات عنصر در <star-rating> جایگزین می‌شوند که ارزش ویژگی slot با مقدار نام <slot> مطابقت دارد، در صورت وجود. اگر نه، محتویات اسلات های قالب نمایش داده می شود.

محتوای یک عنصر سفارشی که با یک شکاف مرتبط نیست - <p>Is this text visible?</p> در سومین <star-rating> ما — در ریشه سایه گنجانده نشده است و بنابراین نمایش داده نمی شود.

ما عنصر سفارشی به نام star-rating را با گسترش HTMLElement تعریف می کنیم:

customElements.define('star-rating',
  class extends HTMLElement {
    constructor() {
      super(); // Always call super first in constructor
      const starRating = document.getElementById('star-rating-template').content;
      const shadowRoot = this.attachShadow({
        mode: 'open'
      });
      shadowRoot.appendChild(starRating.cloneNode(true));
    }
  });

اکنون که عنصر تعریف شده است، هر بار که مرورگر با عنصر <star-rating> مواجه می شود، همانطور که توسط عنصر با #star-rating-template که الگوی ما است، رندر می شود. مرورگر یک درخت DOM سایه را به گره متصل می‌کند و یک کلون از محتویات الگو را به آن DOM سایه اضافه می‌کند. توجه داشته باشید که عناصری که می‌توانید بر روی آن‌ها attachShadow() محدود هستند .

const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.appendChild(starRating.cloneNode(true));

اگر نگاهی به ابزارهای توسعه دهنده بیندازید، متوجه خواهید شد که <form> از <template> بخشی از ریشه سایه هر عنصر سفارشی است. یک کلون از محتویات <template> در هر عنصر سفارشی در ابزارهای توسعه دهنده آشکار است و در مرورگر قابل مشاهده است، اما محتویات خود عنصر سفارشی به صفحه نمایش داده نمی شود.

اسکرین شات DevTools که محتویات قالب کلون شده را در هر عنصر سفارشی نشان می دهد.

در مثال <template> ، محتویات الگو را به بدنه سند اضافه کردیم و محتوا را به DOM معمولی اضافه کردیم. در تعریف customElements ، از همان appendChild() استفاده کردیم، اما محتویات قالب شبیه‌سازی‌شده به یک DOM سایه‌ای کپسوله‌شده اضافه شد.

توجه کنید که چگونه ستاره‌ها به دکمه‌های رادیویی بدون استایل برگشتند؟ به عنوان بخشی از یک DOM سایه به جای DOM استاندارد، یک ظاهر طراحی در برگه CSS Codepen اعمال نمی شود. استایل‌های CSS آن برگه به ​​سند، نه به DOM سایه‌دار، محدود می‌شوند، بنابراین استایل‌ها اعمال نمی‌شوند. ما باید استایل‌های محصور شده را ایجاد کنیم تا محتوای Shadow DOM محصور شده خود را استایل دهی کنیم.

سایه DOM

Shadow DOM سبک‌های CSS را به هر درخت سایه می‌پوشاند و آن را از بقیه سند جدا می‌کند. این بدان معنی است که CSS خارجی برای مؤلفه شما اعمال نمی شود، و سبک های مؤلفه تأثیری بر بقیه سند ندارند، مگر اینکه ما عمداً آنها را به آن هدایت کنیم.

از آنجایی که ما محتویات را به یک DOM سایه اضافه کرده‌ایم، می‌توانیم یک عنصر <style> را اضافه کنیم که CSS محصور شده را به عنصر سفارشی ارائه می‌کند.

با توجه به عنصر سفارشی، لازم نیست نگران نفوذ سبک ها به بقیه سند باشیم. ما می توانیم به طور قابل ملاحظه ای ویژگی انتخابگرها را کاهش دهیم. برای مثال، از آنجایی که تنها ورودی‌های مورد استفاده در عنصر سفارشی دکمه‌های رادیویی هستند، می‌توانیم input به‌جای input[type="radio"] به عنوان انتخابگر استفاده کنیم.

 <template id="star-rating-template">
  <style>
    rating {
      display: inline-flex;
    }
    input {
      appearance: none;
      margin: 0;
      box-shadow: none;
    }
    input::after {
      content: '\2605'; /* solid star */
      font-size: 32px;
    }
    rating:hover input:invalid::after,
    rating:focus-within input:invalid::after {
      color: #888;
    }
    input:invalid::after,
      rating:hover input:hover ~ input:invalid::after,
      input:focus ~ input:invalid::after  {
      color: #ddd;
    }
    input:valid {
      color: orange;
    }
    input:checked ~ input:not(:checked)::after {
      color: #ccc;
      content: '\2606'; /* hollow star */
    }
  </style>
  <form>
    <fieldset>
      <slot name="star-rating-legend">
        <legend>Rate your experience:</legend>
      </slot>
      <rating>
        <input type="radio" name="rating" value="1" aria-label="1 star" required/>
        <input type="radio" name="rating" value="2" aria-label="2 stars"/>
        <input type="radio" name="rating" value="3" aria-label="3 stars"/>
        <input type="radio" name="rating" value="4" aria-label="4 stars"/>
        <input type="radio" name="rating" value="5" aria-label="5 stars"/>
      </rating>
    </fieldset>
    <button type="reset">Reset</button>
    <button type="submit">Submit</button>
  </form>
</template>

در حالی که مؤلفه‌های وب با نشانه‌گذاری درون <template> محصور شده‌اند و سبک‌های CSS در دامنه DOM سایه قرار می‌گیرند و از همه چیز خارج از مؤلفه‌ها پنهان می‌شوند، محتوای اسلات که رندر می‌شود، <anyElement slot="star-rating-legend"> بخشی از <star-rating> ، کپسوله نشده است.

طراحی خارج از محدوده فعلی

امکان استایل دادن به سند از داخل یک DOM سایه دار و استایل دادن به محتوای یک DOM سایه از سبک های جهانی وجود دارد، اما ساده نیست. مرز سایه، جایی که DOM سایه به پایان می رسد و DOM معمولی شروع می شود، قابل عبور است، اما فقط به صورت خیلی عمدی.

درخت سایه ، درخت DOM داخل سایه DOM است. ریشه سایه گره ریشه درخت سایه است.

شبه کلاس :host <star-rating> ، عنصر میزبان سایه را انتخاب می کند. میزبان سایه گره DOM است که DOM سایه به آن متصل است. برای هدف قرار دادن فقط نسخه های خاصی از میزبان، از :host() استفاده کنید. این فقط عناصر میزبان سایه را انتخاب می کند که با پارامتر ارسال شده مطابقت دارند، مانند انتخابگر کلاس یا ویژگی. برای انتخاب همه عناصر سفارشی، می‌توانید از star-rating { /* styles */ } در CSS جهانی یا :host(:not(#nonExistantId)) در سبک‌های الگو استفاده کنید. از نظر ویژگی ، CSS جهانی برنده است.

شبه عنصر ::slotted() از داخل سایه DOM از مرز سایه DOM عبور می کند. اگر با انتخابگر مطابقت داشته باشد، یک عنصر شکافدار را انتخاب می کند. در مثال ما، ::slotted(legend) با سه افسانه ما مطابقت دارد.

برای هدف قرار دادن یک DOM سایه از CSS در محدوده جهانی، الگو باید ویرایش شود. ویژگی part می توان به هر عنصری که می خواهید استایل دهید اضافه کرد. سپس از شبه عنصر ::part() برای تطبیق عناصر درون درخت سایه که با پارامتر ارسال شده مطابقت دارد استفاده کنید. لنگر یا عنصر مبدأ برای شبه عنصر، میزبان یا نام عنصر سفارشی است، در این مورد، star-rating . پارامتر مقدار ویژگی part است.

اگر نشانه گذاری الگوی ما به این صورت شروع شد:

<template id="star-rating-template">
  <form part="formPart">
    <fieldset part="fieldsetPart">

ما می توانیم <form> و <fieldset> را با:

star-rating::part(formPart) { /* styles */ }
star-rating::part(fieldsetPart) { /* styles */ }

نام‌های پارت‌ها مشابه کلاس‌ها عمل می‌کنند: یک عنصر می‌تواند چندین نام بخش جدا شده با فاصله داشته باشد، و چندین عنصر می‌توانند نام قطعه یکسانی داشته باشند.

گوگل یک چک لیست فوق العاده برای ایجاد عناصر سفارشی دارد. همچنین ممکن است بخواهید در مورد DOMهای سایه اظهاری بیاموزید.

درک خود را بررسی کنید

دانش خود را در مورد الگو، اسلات و سایه آزمایش کنید.

به‌طور پیش‌فرض، سبک‌های خارج از سایه DOM به عناصر داخل استایل می‌دهد.

درست است.
دوباره امتحان کنید.
نادرست
درسته!

کدام پاسخ توضیح صحیح عنصر <template> است؟

یک عنصر عمومی که برای نمایش هر محتوایی در صفحه شما استفاده می شود.
دوباره امتحان کنید.
یک عنصر نگهدارنده مکان.
دوباره امتحان کنید.
عنصری که برای اعلان قطعات HTML استفاده می شود که به طور پیش فرض رندر نمی شود.
درسته!