Komponenty instruktażowe – pole wyboru

Podsumowanie

<howto-checkbox> reprezentuje opcję wartości logicznej w formularzu. Najpopularniejszy typ pola wyboru są podwójnym typem, który umożliwia użytkownikowi przełączanie się opcje wyboru – zaznaczone i odznaczone.

Element próbuje samodzielnie zastosować atrybuty role="checkbox" i tabindex="0" po jego utworzeniu. Atrybut role pomaga wspomagać takich jak czytnik ekranu, informuje użytkownika o tym rodzaju kontroli. Atrybut tabindex ustawia kolejność kart, przez co element staje się klawiaturą które można zaznaczyć i łatwo działa. Więcej informacji o tych dwóch tematach znajdziesz Co potrafi ARIA? i Korzystanie z tabindex.

Zaznaczenie tego pola powoduje dodanie atrybutu logicznego checked i ustawienie odpowiednią właściwość checked funkcji true. Dodatkowo element ustawia aria-checked do "true" lub "false", w zależności od stanu. Kliknięcie pola wyboru myszą lub spację wybranych stanów.

Pole wyboru obsługuje też stan disabled. Jeśli właściwość disabled ma wartość prawda lub zostaje zastosowany atrybut disabled, pole wyboru ustawia się aria-disabled="true", usuwa atrybut tabindex i przywraca fokus do dokumentu, jeśli pole wyboru jest bieżące pole activeElement.

Pole wyboru jest sparowane z elementem howto-label, aby mieć pewność, że zawiera ono nazwa ułatwień dostępu.

Dokumentacja

Wersja demonstracyjna

Zobacz demonstrację na żywo w GitHubie

Przykład użycia

<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>

Kod

(function() {

Zdefiniuj kody klawiszy, które ułatwiają obsługę zdarzeń klawiatury.

  const KEYCODE = {
    SPACE: 32,
  };

Klonowanie treści z elementu <template> jest skuteczniejsze niż innerHTML, ponieważ pozwala uniknąć dodatkowych kosztów analizy 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'];
    }

Konstruktor elementu jest uruchamiany przy każdym utworzeniu nowej instancji. Instancje są tworzone przez analizę kodu HTML, wywołanie document.createElement('howto-checkbox') lub przez wywołanie nowego narzędzia HowToCheckbox(); Konstruktor to dobre miejsce do tworzenia shadow DOM, ale lepiej unikać dotykania atrybutów ani elementów podrzędnych Light DOM, ponieważ mogą one nie być jeszcze dostępne.

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

Pole connectedCallback() jest uruchamiane po wstawieniu elementu do DOM. To dobre miejsce na skonfigurowanie początkowych detektorów zdarzeń role, tabindex, stanu wewnętrznego i instalacji.

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

Użytkownik może ustawić właściwość w wystąpieniu elementu, zanim jego prototyp zostanie połączony z tą klasą. Metoda _upgradeProperty() sprawdza właściwości instancji i uruchamia je przy użyciu odpowiednich ustawień klas. Więcej informacji znajdziesz w sekcji o właściwościach leniwych.

      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;
      }
    }

Interfejs disconnectedCallback() jest uruchamiany po usunięciu elementu z DOM. To dobre miejsce na porządki, takie jak opublikowanie plików referencyjnych i usunięcie detektorów zdarzeń.

    disconnectedCallback() {
      this.removeEventListener('keyup', this._onKeyUp);
      this.removeEventListener('click', this._onClick);
    }

Właściwości i odpowiadające im atrybuty powinny być takie same. Metoda ustawiania właściwości dla zaznaczonego pola obsługuje wartości prawda/fałszywe i odzwierciedla je zgodnie ze stanem atrybutu. Więcej informacji znajdziesz w sekcji na temat unikania ponownego przesyłania.

    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');
    }

Funkcja attributeChangedCallback() jest wywoływana, gdy dowolny z atrybutów w tablicy zaobserwowany atrybut ulegnie zmianie. Możesz tam radzić sobie z efektami ubocznymi, takimi jak ustawianie atrybutów 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);

Atrybut tabindex nie umożliwia pełnego usunięcia zaznaczenia elementu. Elementy z atrybutem tabindex=-1 nadal można zaznaczyć za pomocą myszy lub wywołując funkcję focus(). Aby mieć pewność, że element jest wyłączony i nie można go zaznaczyć, usuń atrybut tabindex.

          if (hasValue) {
            this.removeAttribute('tabindex');

Jeśli zaznaczenie jest obecnie ustawione na tym elemencie, cofnij jego fokus, wywołując metodę HTMLElement.blur()

            this.blur();
          } else {
            this.setAttribute('tabindex', '0');
          }
          break;
      }
    }

    _onKeyUp(event) {

Nie używaj skrótów modyfikujących, które są zwykle używane przez technologię wspomagającą osoby z niepełnosprawnością.

      if (event.altKey)
        return;

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

Każde inne naciśnięcie klawisza jest ignorowane i przekazywane z powrotem do przeglądarki.

        default:
          return;
      }
    }

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

_toggleChecked() wywołuje zaznaczoną metodę ustawiającą i odwraca jej stan. Zdarzenie _toggleChecked() jest wywoływane tylko przez działanie użytkownika, dlatego powoduje też wysłanie zdarzenia zmiany. To zdarzenie wyświetla się jako dymki, by naśladować natywne działanie <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);
})();