סיכום
הערך <howto-checkbox>
מייצג אפשרות בוליאנרית בטופס. הסוג הנפוץ ביותר של תיבת סימון הוא תיבת סימון עם שני מצבים, שמאפשרת למשתמש לעבור בין שתי אפשרויות – מסומנת ולא מסומנת.
הרכיב מנסה להחיל את המאפיינים role="checkbox"
ו-tabindex="0"
על עצמו בפעם הראשונה שהוא נוצר. המאפיין role
עוזר לטכנולוגיות מסייעות, כמו קורא מסך, לומר למשתמש איזה סוג של אמצעי בקרה זה. המאפיין tabindex
מוסיף את הרכיב לסדר הקלדת Tab, כך שניתן להתמקד בו באמצעות המקלדת ולהפעיל אותו. למידע נוסף על שני הנושאים האלה, אפשר לעיין במאמרים מה אפשר לעשות באמצעות ARIA? ושימוש ב-tabindex.
כשתיבת הסימון מסומנת, המערכת מוסיפה מאפיין בוליאני checked
ומגדירה את checked
התואם ל-true
. בנוסף, הרכיב מגדיר את המאפיין aria-checked
לערך "true"
או "false"
, בהתאם למצב שלו. לחיצה על תיבת הסימון בעזרת העכבר או מקש הרווח משנה את המצבים האלה.
תיבת הסימון תומכת גם במצב disabled
. אם המאפיין disabled
מוגדר כ-true או שהמאפיין disabled
מיושם, התיבה מסמנת את aria-disabled="true"
, מסירה את המאפיין tabindex
ומחזירה את המיקוד למסמך אם התיבה היא activeElement
הנוכחי.
תיבת הסימון מותאמת לרכיב howto-label
כדי לוודא שיש לה שם נגיש.
חומרי עזר
- איך משתמשים ברכיבים ב-GitHub
- דפוס של תיבת סימון במסמך ARIA Authoring Practices 1.1
- מה אפשר לעשות עם ARIA?
- שימוש ב-tabindex
הדגמה (דמו)
דוגמה לשימוש
<style>
howto-checkbox {
vertical-align: middle;
}
howto-label {
vertical-align: middle;
display: inline-block;
font-weight: bold;
font-family: sans-serif;
font-size: 20px;
margin-left: 8px;
}
</style>
<howto-checkbox id="join-checkbox"></howto-checkbox>
<howto-label for="join-checkbox">Join Newsletter</howto-label>
קוד
(function() {
הגדרת קודי מפתחות כדי לעזור בטיפול באירועים של מקלדת.
const KEYCODE = {
SPACE: 32,
};
העתקת תוכן מרכיב <template>
משפרת את הביצועים בהשוואה לשימוש ב-innerHTML, כי היא מונעת עלויות נוספות של ניתוח HTML.
const template = document.createElement('template');
template.innerHTML = `
<style>
:host {
display: inline-block;
background: url('../images/unchecked-checkbox.svg') no-repeat;
background-size: contain;
width: 24px;
height: 24px;
}
:host([hidden]) {
display: none;
}
:host([checked]) {
background: url('../images/checked-checkbox.svg') no-repeat;
background-size: contain;
}
:host([disabled]) {
background:
url('../images/unchecked-checkbox-disabled.svg') no-repeat;
background-size: contain;
}
:host([checked][disabled]) {
background:
url('../images/checked-checkbox-disabled.svg') no-repeat;
background-size: contain;
}
</style>
`;
class HowToCheckbox extends HTMLElement {
static get observedAttributes() {
return ['checked', 'disabled'];
}
ה-constructor של הרכיב מופעל בכל פעם שנוצרת מופע חדש. אפשר ליצור את המופעים על ידי ניתוח HTML, קריאה ל-document.createElement('howto-checkbox') או קריאה ל-new HowToCheckbox();. ה-constructor הוא מקום טוב ליצירת DOM בצל, אבל כדאי להימנע משינוי מאפיינים או צאצאים של DOM בהיר, כי יכול להיות שהם עדיין לא יהיו זמינים.
constructor() {
super();
this.attachShadow({mode: 'open'});
this.shadowRoot.appendChild(template.content.cloneNode(true));
}
האירוע connectedCallback()
מופעל כשהרכיב מוכנס ל-DOM. זהו מקום טוב להגדיר את המצב הפנימי role
, tabindex
הראשוני ולהתקין מאזינים לאירועים.
connectedCallback() {
if (!this.hasAttribute('role'))
this.setAttribute('role', 'checkbox');
if (!this.hasAttribute('tabindex'))
this.setAttribute('tabindex', 0);
משתמש יכול להגדיר מאפיין למופיע של רכיב לפני שהאב טיפוס שלו מחובר לכיתה הזו. השיטה _upgradeProperty()
תבדוק אם יש מאפייני מופעים ותריץ אותם דרך הגדרות ה-set של הכיתה המתאימה. פרטים נוספים זמינים בקטע מאפיינים מושהה.
this._upgradeProperty('checked');
this._upgradeProperty('disabled');
this.addEventListener('keyup', this._onKeyUp);
this.addEventListener('click', this._onClick);
}
_upgradeProperty(prop) {
if (this.hasOwnProperty(prop)) {
let value = this[prop];
delete this[prop];
this[prop] = value;
}
}
האירוע disconnectedCallback()
מופעל כשהרכיב מוסר מה-DOM. זהו מקום טוב לבצע פעולות ניקוי כמו שחרור הפניות והסרת מאזינים לאירועים.
disconnectedCallback() {
this.removeEventListener('keyup', this._onKeyUp);
this.removeEventListener('click', this._onClick);
}
המאפיינים והמאפיינים התואמים שלהם צריכים להיות זהים. ה-setter של המאפיין checked מטפל בערכים נכונים/לא נכונים ומשקף אותם במצב של המאפיין. פרטים נוספים זמינים בקטע הימנעות מחדירה חוזרת.
set checked(value) {
const isChecked = Boolean(value);
if (isChecked)
this.setAttribute('checked', '');
else
this.removeAttribute('checked');
}
get checked() {
return this.hasAttribute('checked');
}
set disabled(value) {
const isDisabled = Boolean(value);
if (isDisabled)
this.setAttribute('disabled', '');
else
this.removeAttribute('disabled');
}
get disabled() {
return this.hasAttribute('disabled');
}
הפונקציה attributeChangedCallback()
נקראת כשאחד מהמאפיינים במערך observedAttributes משתנה. זהו מקום טוב לטיפול בתופעות לוואי, כמו הגדרת מאפייני ARIA.
attributeChangedCallback(name, oldValue, newValue) {
const hasValue = newValue !== null;
switch (name) {
case 'checked':
this.setAttribute('aria-checked', hasValue);
break;
case 'disabled':
this.setAttribute('aria-disabled', hasValue);
המאפיין tabindex
לא מספק דרך להסיר לחלוטין את היכולת להתמקד ברכיב. עדיין אפשר להתמקד ברכיבים עם tabindex=-1
באמצעות העכבר או באמצעות focus()
. כדי לוודא שרכיב מושבת ולא ניתן להתמקד בו, מסירים את המאפיין tabindex
.
if (hasValue) {
this.removeAttribute('tabindex');
אם המיקוד נמצא כרגע ברכיב הזה, מבטלים את המיקוד על ידי קריאה ל-method HTMLElement.blur()
this.blur();
} else {
this.setAttribute('tabindex', '0');
}
break;
}
}
_onKeyUp(event) {
לא מטפלים במקשי קיצור עם שינוי (modifier) שבדרך כלל משמשים לטכנולוגיה מסייעת.
if (event.altKey)
return;
switch (event.keyCode) {
case KEYCODE.SPACE:
event.preventDefault();
this._toggleChecked();
break;
המערכת תתעלם מלחיצות על מקשים אחרים ותעביר אותן בחזרה לדפדפן.
default:
return;
}
}
_onClick(event) {
this._toggleChecked();
}
_toggleChecked()
קורא למגדיר המאומת ומשנה את המצב שלו. מכיוון ש-_toggleChecked()
נגרם רק כתוצאה מפעולת משתמש, הוא גם ישלח אירוע שינוי. האירוע הזה עובר דרך ה-bubbles כדי לחקות את ההתנהגות המקורית של <input type=checkbox>
.
_toggleChecked() {
if (this.disabled)
return;
this.checked = !this.checked;
this.dispatchEvent(new CustomEvent('change', {
detail: {
checked: this.checked,
},
bubbles: true,
}));
}
}
customElements.define('howto-checkbox', HowToCheckbox);
})();