רכיבי HowTo – תיבת סימון של מדריך

סיכום

<howto-checkbox> מייצג אפשרות בוליאנית בטופס. הסוג הנפוץ ביותר של תיבת הסימון הוא סוג כפול שמאפשר למשתמש לעבור בין הבחירות – מסומנות ולא מסומנות.

הרכיב מנסה להחיל באופן עצמאי את המאפיינים role="checkbox" ו tabindex="0" בפעם הראשונה שבה הוא נוצר. המאפיין role עוזר טכנולוגיה כמו קורא מסך שאומרים למשתמש מהו סוג השליטה הזה. המאפיין tabindex מצרף את הרכיב לסידור הכרטיסיות וכך הוא הופך למקלדת שניתן להתמקד בהם ולבצע בהם פעולות. למידע נוסף על שני הנושאים האלה, אפשר להיכנס ל מה אפשר לעשות בעזרת ARIA? ובאמצעות Tabindex.

כשמסמנים את התיבה, היא מוסיפה מאפיין בוליאני checked ומגדירה נכס checked תואם ל-true. בנוסף, הרכיב מגדיר aria-checked של "true" או "false", בהתאם . לחיצה על תיבת סימון עם העכבר או מקש הרווח כדי להחליף את המצבים את המצבים המסומנים בכוכב.

בתיבת הסימון יש תמיכה גם במצב disabled. אם אחד מהמאפיינים disabled מוגדר כ-True או שהוחל המאפיין disabled, תיבות הסימון aria-disabled="true", מסירה את המאפיין tabindex ומחזירה את המיקוד למסמך אם תיבת הסימון היא activeElement הנוכחי.

תיבת הסימון מותאמת לרכיב howto-label כדי לוודא שיש בה שם נגיש.

חומרי עזר

הדגמה (דמו)

צפייה בהדגמה בזמן אמת ב-GitHub

שימוש לדוגמה

<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('US-checkbox') או קריאה ל-HowToCheckbox(); ה-constructor הוא מקום טוב ליצירת DOM של צללים, אבל כדאי להימנע מנגיעה במאפיינים או צאצאים של DOM קל כי הם עדיין לא זמינים.

    constructor() {
      super();
      this.attachShadow({mode: 'open'});
      this.shadowRoot.appendChild(template.content.cloneNode(true));
    }

connectedCallback() מופעל כשמוסיפים את הרכיב ל-DOM. זהו מקום טוב להגדרת פונקציות role, tabindex והמצב הפנימי הראשוניות והתקנת פונקציות event listener.

    connectedCallback() {
      if (!this.hasAttribute('role'))
        this.setAttribute('role', 'checkbox');
      if (!this.hasAttribute('tabindex'))
        this.setAttribute('tabindex', 0);

משתמש יכול להגדיר מאפיין במופע של אלמנט, לפני שאבי הטיפוס שלו חובר למחלקה הזו. ה-method _upgradeProperty() תבדוק אם יש מאפייני מכונה ותריץ אותם באמצעות מגדיר המחלקות המתאים. פרטים נוספים זמינים בקטע נכסים מדורגת.

      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 של המאפיין (property) עבור מסומן מטפל בערכי truthy/falsy ומשקף אותם למצב המאפיין. לפרטים נוספים, אפשר לעיין בקטע מניעת הרשמה מחדש.

    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() תופעל בכל שינוי של אחד מהמאפיינים במערך תצפית על תכונות. זה מקום טוב לטיפול בתופעות לוואי, כמו הגדרת מאפייני 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) {

אל תטפלו בקיצורי דרך של מקשי קיצור שמשמשים בדרך כלל על ידי טכנולוגיה מסייעת.

      if (event.altKey)
        return;

      switch (event.keyCode) {
        case KEYCODE.SPACE:
          event.preventDefault();
          this._toggleChecked();
          break;

המערכת מתעלמת מכל הקשה אחרת על מקש, ומועברת חזרה לדפדפן.

        default:
          return;
      }
    }

    _onClick(event) {
      this._toggleChecked();
    }

הפונקציה _toggleChecked() מפעילה את המגדיר המסומן והופכת את המצב שלו. _toggleChecked() נגרמה רק על ידי פעולת משתמש, ולכן היא גם תשלח אירוע שינוי. האירוע הזה מופיע בבועות כדי לחקות את ההתנהגות המקורית של <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);
})();