HowTo-Komponenten – HowTo-Kästchen

Zusammenfassung

Ein <howto-checkbox> steht für eine boolesche Option in einem Formular. Der gängigste Typ von Kästchen ist ein Dual-Type, bei dem der Nutzer zwischen zwei Optionen wechseln kann: aktiviert oder deaktiviert.

Beim Erstellen des Elements wird versucht, die Attribute role="checkbox" und tabindex="0" selbst anzuwenden. Mithilfe des role-Attributs kann dem Nutzer mithilfe von Hilfstechnologien wie einem Screenreader mitgeteilt werden, welche Art von Steuerelement das ist. Durch das Attribut tabindex wird das Element für die TAB-Reihenfolge aktiviert, sodass es über die Tastatur fokussiert und bedient werden kann. Weitere Informationen zu diesen beiden Themen finden Sie unter Was kann ARIA tun? und Tabindex verwenden.

Wenn das Kästchen angeklickt ist, wird das boolesche Attribut checked hinzugefügt und ein entsprechendes checked-Attribut auf true festgelegt. Darüber hinaus setzt das Element je nach Status ein aria-checked-Attribut auf "true" oder "false". Wenn Sie mit der Maus oder der Leertaste auf das Kästchen klicken, werden diese aktivierten Status geändert.

Das Kästchen unterstützt auch den Status disabled. Wenn das Attribut disabled entweder auf „true“ gesetzt ist oder das Attribut disabled angewendet wird, wird über das Kästchen aria-disabled="true" festgelegt, das Attribut tabindex wird entfernt und der Fokus wird auf das Dokument zurückgesetzt, wenn es der aktuelle activeElement ist.

Das Kästchen wird mit einem howto-label-Element gekoppelt, damit es einen zugänglichen Namen hat.

Referenz

Demo

Live-Demo auf GitHub ansehen

Anwendungsbeispiel

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

Code

(function() {

Definieren Sie Tastencodes für die Verarbeitung von Tastaturereignissen.

  const KEYCODE = {
    SPACE: 32,
  };

Das Klonen von Inhalten aus einem <template>-Element ist leistungsfähiger als die Verwendung von innerHTML, da zusätzliche Kosten für das HTML-Parsing vermieden werden.

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

Der Konstruktor des Elements wird immer dann ausgeführt, wenn eine neue Instanz erstellt wird. Instanzen werden entweder durch Parsen von HTML-Code, Aufrufen von document.createElement('howto-checkbox') oder dem Aufrufen von neuen HowToCheckbox() erstellt. Der Konstruktor eignet sich gut zum Erstellen eines Schatten-DOM. Dennoch sollten Sie es vermeiden, Attribute oder Light DOM-Child-Elemente zu berühren, da diese möglicherweise noch nicht verfügbar sind.

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

connectedCallback() wird ausgelöst, wenn das Element in das DOM eingefügt wird. Hier können Sie die anfängliche role und die tabindex sowie den internen Status festlegen und Event-Listener installieren.

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

Ein Nutzer kann eine Eigenschaft für eine Instanz eines Elements festlegen, bevor sein Prototyp mit dieser Klasse verbunden wird. Die Methode _upgradeProperty() sucht nach Instanzattributen und führt sie mit den richtigen Klassensetters aus. Weitere Informationen

      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() wird ausgelöst, wenn das Element aus dem DOM entfernt wird. Dies ist ein guter Ort, um Bereinigungsarbeiten wie das Freigeben von Referenzen und das Entfernen von Ereignis-Listenern durchzuführen.

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

Eigenschaften und die zugehörigen Attribute sollten einander spiegeln. Der Eigenschafts-Setter für aktiviert verarbeitet Wahr-/Fälschungen und spiegelt diese dem Status des Attributs wider. Weitere Informationen

    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() wird aufgerufen, wenn sich eines der Attribute im Array „BeobachtetAttributes“ ändert. Hier können Sie gut mit Nebeneffekten umgehen, z. B. beim Festlegen von ARIA-Attributen.

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

Das Attribut tabindex bietet keine Möglichkeit, die Fokussierbarkeit eines Elements vollständig zu entfernen. Elemente mit tabindex=-1 können weiterhin mit der Maus oder durch Aufrufen von focus() fokussiert werden. Entfernen Sie das Attribut tabindex, damit ein Element deaktiviert und nicht fokussierbar ist.

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

Wenn das Element derzeit im Fokus ist, heben Sie den Fokus auf, indem Sie die Methode HTMLElement.blur() aufrufen.

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

    _onKeyUp(event) {

Umgang mit Modifikatortasten, die normalerweise von Hilfstechnologien verwendet werden, werden nicht berücksichtigt.

      if (event.altKey)
        return;

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

Alle anderen Tastenbetätigungen werden ignoriert und an den Browser zurückgegeben.

        default:
          return;
      }
    }

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

_toggleChecked() ruft den überprüften Setter auf und dreht seinen Status. Da _toggleChecked() nur durch eine Nutzeraktion verursacht wird, wird auch ein Änderungsereignis ausgelöst. Dieses Ereignis wird angezeigt, um das native Verhalten von „<input type=checkbox>“ zu imitieren.

    _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);
})();