عناصر وب دسترس‌پذیر

ویژگی‌های وب: بازتاب شاخصه ARIA Shadow DOM

مرز سایه نحوه عملکرد معناشناسی و ARIA را تغییر می‌دهد. دو خرابی غالب است: معناشناسی ازدست‌رفته در داخل درخت سایه، و ارجاعات ARIA که بی‌صدا شکسته می‌شوند زیرا نمی‌توانند از ریشه‌های سایه عبور کنند.

از نشانه‌گذاری معنایی شروع کنید: از <button>، <nav>، <ul> واقعی در الگو استفاده کنید تا بیشتر معناشناسی‌ها به‌طور رایگان ارائه شوند و ElementInternals/ARIA را برای شکاف‌ها رزرو کنید.

معناشناسی پیش‌فرض ازطریق ترکیب ARIA ElementInternals

نقش عنصر سفارشی و وضعیت ARIA را در JS ازطریق ElementInternals تنظیم کنید، به‌جای اینکه مشخصه‌های ARIA را روی میزبان بنویسید. این کار باعث می‌شود معناشناسی به‌عنوان پیش‌فرض‌هایی که مؤلفه مالک آن است حفظ شود، درحالی‌که همچنان به مصرف‌کننده اجازه می‌دهد آن‌ها را با مشخصه‌هایی در Light DOM لغو کند.

class ToggleSwitch extends HTMLElement {
  #internals;
  constructor() {
    super();
    this.#internals = this.attachInternals();
    this.#internals.role = 'switch';          // default role, no host attribute needed
    this.#internals.ariaChecked = 'false';    // exposed to the accessibility tree
    this.addEventListener('click', () => this.toggle());
    this.addEventListener('keydown', (e) => {
      if (e.key === ' ') { e.preventDefault(); this.toggle(); }
    });
  }
  connectedCallback() {
    // Put the host in the tab order on connect (setting tabIndex in the constructor throws on createElement).
    if (!this.hasAttribute('tabindex')) this.tabIndex = 0;
  }
  toggle() {
    const on = this.#internals.ariaChecked !== 'true';
    this.#internals.ariaChecked = String(on);
  }
}
customElements.define('toggle-switch', ToggleSwitch);

این کار از آلوده شدن DOM با ویژگی‌های ARIA جلوگیری می‌کند و مانع از آن می‌شود که مصرف‌کنندگان به‌طور تصادفی آن‌ها را خراب کنند، درحالی‌که درخت دسترس‌پذیری همچنان نقش و وضعیت صحیح را می‌بیند.

مدیریت کردن ناحیه تمرکز و delegatesFocus

  • وقتی خود عنصر میزبان کنترل تعاملی است و نقش و وضعیت را ازطریق ElementInternals (مانند ToggleSwitch در بالا) حمل می‌کند، میزبان را در ترتیب برگه داخل connectedCallback قرار دهید (if (!this.hasAttribute('tabindex')) this.tabIndex = 0 — هرگز در سازنده، که روی document.createElement پرتاب می‌شود) تا تمرکز روی میزبان قرار گیرد و فناوری کمکی نقش و وضعیت آن را اعلام کند. tabindex را در میزبان برای مؤلفه‌های ایستا و غیرتعاملی (مانند فراخوان یا کارت) تنظیم نکنید.
  • وقتی مؤلفه کنترل قابل‌تمرکز داخلی را دربرمی‌گیرد که مالک معناشناسی است (یا در عنصر سفارشی مرتبط با فرم با static formAssociated = true)، ریشه سایه را با delegatesFocus: true پیوست کنید. سپس تمرکز روی میزبان (و کلیک کردن روی <label> مرتبط برای عناصر مرتبط با فرم) تمرکز را به اولین فرزند قابل‌تمرکز هدایت می‌کند و :focus با میزبان مطابقت دارد. delegatesFocus: true را با نقش/وضعیت در میزبان ترکیب نکنید، درغیراین‌صورت تمرکز روی عنصر داخلی قرار می‌گیرد و وضعیت میزبان نادیده گرفته می‌شود.
  • برای تمرکز متحرک یا انتقال تمرکز به ناحیه جدیداً آشکارشده، به هدف tabindex="-1" بدهید و .focus() را فراخوانی کنید؛ اهداف قابل‌تمرکز ازطریق برنامه‌نویسی را به‌جای تکیه بر ترتیب کلید جهش صریح کنید.

روابط ARIA در ریشه‌های سایه

  • اجباری: id ارجاع‌شده توسط aria-labelledby، aria-describedby، aria-controls، و غیره فقط در درخت خود عنصر ارجاع‌دهنده حل می‌شود. مشخصه مبتنی بر id در Light DOM نمی‌تواند به id داخل درخت سایه اشاره کند و برعکس؛ مرجع بدون اطلاع نادیده گرفته می‌شود.
  • برای عبور از مرز، از ویژگی‌های عنصر ARIA منعکس‌شده (مثلاً ariaLabelledByElements، ariaDescribedByElements، و غیره) استفاده کنید که به‌جای شناسه‌ها، ارجاع‌های مستقیم عنصر JS را می‌گیرند. این ارجاع‌ها به‌سمت بیرون حل‌وفصل می‌شوند: یک عنصر درون درخت سایه می‌تواند به هدفی در Light DOM یا هر درخت اجدادی (ازجمله محتوای شکاف‌دار) اشاره کند، بنابراین می‌توانید یک کنترل داخلی سایه را از محتوای Light DOM برچسب‌گذاری کنید — چیزی که ارجاع‌های id نمی‌توانند بیان کنند.
  • آن‌ها به‌سمت داخل حل نمی‌شوند: یک عنصر نمی‌تواند به یک درخت سایه فرزند ارجاع دهد (مثلاً یک میزبان نمی‌تواند توسط محتوای سایه خودش برچسب‌گذاری شود). برای این جهت، سازوکار نوظهور هدف مرجع جهت استانداردها است، اما هنوز به‌طور گسترده دردسترس نیست.
  • وقتی رابطه‌ای را نمی‌توان به هیچ‌یک از این دو روش بیان کرد، آن را در یک درخت نگه دارید (برچسب را در همان درخت ارائه دهید) یا به‌جای مرجع id از نوشتار aria-label استفاده کنید.

راهبردهای جایگزین

در جایی که ElementInternals ARIA mixin دردسترس نیست، نقش و وضعیت ARIA را به‌عنوان ویژگی در میزبان تنظیم کنید (this.setAttribute('role', 'switch')). این روش در همه‌جا کار می‌کند، اما هزینه آن قرار دادن ویژگی‌ها در DOM است که مصرف‌کننده می‌تواند آن‌ها را لغو کند.

در جایی که ویژگی‌های عنصر ARIA منعکس‌شده دردسترس نیست، از شاخصه aria-* مبتنی بر id برای روابط درخت یکسان استفاده کنید؛ برچسب بین‌مرزی معادل مبتنی بر id ندارد، بنابراین به نوشتار aria-label برگردید.