يغيّر حدّ الظل طريقة عمل الدلالات وARIA. هناك نوعان من حالات الإخفاق السائدة: دلالات مفقودة داخل شجرة الظل، ومراجع ARIA التي تتعطل بصمت لأنها لا يمكنها عبور جذور الظل.
ابدأ من الترميز الدلالي: استخدِم عناصر <button> و<nav> و<ul> حقيقية داخل النموذج لكي تظهر معظم الدلالات مجانًا، واحتفِظ بـ ElementInternals/ARIA للفجوات.
الدلالات التلقائية من خلال مزيج ElementInternals ARIA
يمكنك ضبط دور عنصر مخصّص وحالة 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على المضيف للعناصر الثابتة غير التفاعلية (مثل وسيلة الشرح أو البطاقة). - يجب إرفاق جذر الظل باستخدام
delegatesFocus: trueعندما يغلّف المكوّن عنصر تحكّم داخليًا يمكن التركيز عليه يملك الدلالات (أو على عنصر مخصّص مرتبط بنموذج باستخدامstatic formAssociated = 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التعبير عنه. - لا يتم حلّها داخليًا: لا يمكن لعنصر أن يشير إلى شجرة ظل تابعة (على سبيل المثال، لا يمكن تصنيف مضيف من خلال محتوى الظل الخاص به). لهذا الغرض، تُعدّ آلية Reference Target الناشئة هي الاتجاه المعتمد، ولكنّها غير متاحة على نطاق واسع بعد.
- عندما يتعذّر التعبير عن علاقة بأي من الطريقتين، احتفِظ بها ضمن شجرة واحدة (اعرض التصنيف في الشجرة نفسها) أو استخدِم نص
aria-labelبدلاً من مرجعid.
استراتيجيات احتياطية
في حال عدم توفّر مزيج ARIA ElementInternals، اضبط الدور وحالة ARIA كسمات على المضيف بدلاً من ذلك (this.setAttribute('role', 'switch')). يعمل هذا الإجراء في كل مكان، ولكنّه يضع سمات في نموذج DOM يمكن للمستهلك إلغاؤها.
في حال عدم توفّر سمات عناصر ARIA المنعكسة، استخدِم سمة aria-* مستندة إلى id للعلاقات في الشجرة نفسها. لا تتوفّر سمة مكافئة مستندة إلى id للتصنيفات التي تتجاوز الحدود، لذا يمكنك استخدام نص aria-label كحل بديل.