Kodokon kodokon.com

مكوّنات الويب: العناصر المخصّصة وShadow DOM والفتحات

أتقن دورة حياة العنصر المخصّص، وتغليف Shadow DOM، وإسقاط المحتوى عبر الفتحات (slots).

12 دقيقة · 3 أسئلة

افتح هذا الدرس في Kodokon

توسّع العناصر المخصّصة (custom elements) مفردات HTML: يمكن تسجيل أي صنف يرث من HTMLElement في CustomElementRegistry، ما دام اسمه يحتوي على شرطة (user-badge، وليس userbadge أبدًا). نقطة دقيقة: إذا ظهر الوسم في المستند قبل استدعاء customElements.define، فإنه يُنشأ أولًا كعنصر مجهول ثم يُرقّى في مكانه - عندئذ يُستدعى المُنشئ لاحقًا على عقدة موجودة سلفًا في الشجرة.

JAVASCRIPT
class UserBadge extends HTMLElement {
  static observedAttributes = ["name"];
  connectedCallback() {
    this.render();
  }
  attributeChangedCallback() {
    this.render();
  }
  render() {
    this.textContent = this.getAttribute("name");
  }
}
customElements.define("user-badge", UserBadge);
عنصر مخصّص بسيط بدورة حياة كاملة.

تخفي دورة الحياة بعض المصائد: لا يُطلَق attributeChangedCallback إلا للسمات المدرجة في observedAttributes، وأثناء الترقية يُستدعى لكل سمة موجودة سلفًا قبل connectedCallback. تمنع المواصفة المُنشئ من فحص السمات والأبناء، أو من إضافة أي منها: في لحظة تشغيله (عبر createElement أو الاستنساخ)، قد يكون العنصر فارغًا تمامًا.

HTML
<template id="card-tpl">
  <style>
    :host { display: block; padding: 1rem; }
    ::slotted(h2) { margin: 0; }
  </style>
  <slot name="title"></slot>
  <slot></slot>
</template>
قالب خامل: مُحلَّل، لكنه لا يُنفَّذ ولا يُعرَض.

ينشئ attachShadow شجرة فرعية مغلّفة: لا تتسرّب الأنماط منها، ولا تصل إليها محدِّدات الصفحة - فقط خصائص CSS الموروثة (color، font) والخصائص المخصّصة تعبر الحدّ. يعيش محتوى <template> في DocumentFragment يمكن الوصول إليه عبر .content: لا تعمل نصوصه البرمجية ولا تُحمَّل صوره حتى تستنسخه في المستند. تُسقَط العُقد الموضوعة في الـ light DOM في عناصر <slot>: تبقى فيزيائيًا في الـ light DOM، ويُخطرك الحدث slotchange بأي تغيير في الإسناد.

JAVASCRIPT
class InfoCard extends HTMLElement {
  constructor() {
    super();
    const tpl = document.getElementById("card-tpl");
    this.attachShadow({ mode: "open" })
      .appendChild(tpl.content.cloneNode(true));
  }
}
customElements.define("info-card", InfoCard);
استنساخ القالب داخل جذر ظلّي مفتوح.

يستحق امتدادان انتباهك. العناصر المخصّصة المرتبطة بالنماذج: مع static formAssociated = true وattachInternals()، يشارك مكوّنك في النماذج مشاركة كاملة (القيمة المُرسَلة، التحقق الأصلي، حالات :invalid). والـ Shadow DOM التصريحي: <template shadowrootmode=open> يُرفق جذرًا ظليًا وقت التحليل، دون JavaScript، ما يجعل مكوّنات الويب متوافقة مع التصيير من جهة الخادم.

اختبار المعرفة

تأكّد من أنك تذكّرت النقاط الأساسية في هذا الدرس.

  1. لماذا يجب على مُنشئ العنصر المخصّص ألّا يقرأ سماته ولا يُنشئ أبناءً؟
    • لأن السمات مُجمّدة للقراءة فقط قبل connectedCallback
    • لأن Shadow DOM يُخفي الـ light DOM أثناء البناء
    • لأنه في تلك اللحظة (createElement، الاستنساخ)، قد يكون العنصر فارغًا؛ تفرض المواصفة مُنشئًا لا أثر له على المحتوى
  2. مع attachShadow({ mode: 'closed' })، ماذا يعيد element.shadowRoot من الخارج؟
    • null: فقط المرجع الذي يعيده attachShadow يتيح الوصول إلى الجذر الظلّي
    • الجذر الظلّي، لكن للقراءة فقط
    • DocumentFragment فارغ
    • يُرمى استثناء DOMException
  3. أين تعيش فيزيائيًا العُقد المُسقَطة في <slot>؟
    • تُنقل إلى Shadow DOM وقت التصيير
    • تبقى في الـ light DOM؛ لا تفعل الفتحة سوى عرضها في موضعها ضمن الشجرة المسطّحة
    • تُستنسخ: يبقى الأصل في الـ light DOM، وتعيش النسخة في Shadow DOM