Composants HowTo – Case à cocher

Résumé

<howto-checkbox> représente une option booléenne dans un formulaire. Le type le plus courant de case à cocher est un double type qui permet à l'utilisateur de basculer entre (cochés ou non).

L'élément tente d'appliquer lui-même les attributs role="checkbox" et tabindex="0" lors de sa création. L'attribut role permet une technologie comme un lecteur d'écran, indiquer à l'utilisateur de quel type de contrôle il s'agit. L'attribut tabindex active l'élément dans l'ordre de tabulation, ce qui en fait un clavier sélectionnables et maniables. Pour en savoir plus sur ces deux sujets, consultez Que peut faire ARIA ? et Utilisation de tabindex.

Lorsque la case est cochée, un attribut booléen checked est ajouté et définit une propriété checked correspondante à true. De plus, l'élément définit une l'attribut aria-checked à "true" ou "false", selon ses de l'état. Si vous cliquez sur la case à cocher avec la souris ou la barre d'espace, cochés.

La case à cocher accepte également un état disabled. Si la propriété disabled est défini sur "true" ou que l'attribut disabled est appliqué, la case à cocher aria-disabled="true", supprime l'attribut tabindex et remet le focus. au document si la case à cocher correspond à la valeur activeElement actuelle.

La case à cocher est associée à un élément howto-label pour garantir qu'elle comporte un nom accessible.

Référence

Démo

Voir la démonstration en direct sur GitHub

Exemple d'utilisation

<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() {

Définissez des codes de touche pour faciliter la gestion des événements de clavier.

  const KEYCODE = {
    SPACE: 32,
  };

Le clonage du contenu à partir d'un élément <template> est plus performant que d'utiliser innerHTML, car cela permet d'éviter des coûts d'analyse HTML supplémentaires.

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

Le constructeur de l'élément est exécuté chaque fois qu'une instance est créée. Les instances sont créées en analysant le code HTML, en appelant document.createElement('howto-checkbox') ou en appelant la méthode new HowToCheckbox(). Le constructeur est idéal pour créer un Shadow DOM, mais vous devez éviter de modifier les attributs ou les enfants Light DOM, car ils ne sont peut-être pas encore disponibles.

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

connectedCallback() se déclenche lorsque l'élément est inséré dans le DOM. C'est un bon endroit pour définir les role et tabindex initiaux, l'état interne et les écouteurs d'événements d'installation.

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

Un utilisateur peut définir une propriété sur une instance d'un élément avant que son prototype ne soit connecté à cette classe. La méthode _upgradeProperty() vérifie les propriétés de l'instance et les exécute via les setters de classe appropriés. Pour en savoir plus, consultez la section Propriétés différées.

      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() se déclenche lorsque l'élément est supprimé du DOM. C'est un bon endroit pour effectuer des tâches de nettoyage comme la libération des références et la suppression des écouteurs d'événements.

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

Les propriétés et leurs attributs correspondants doivent se mettre en miroir. Le setter de la propriété pour la case cochée gère les valeurs exactes/falsifiées et reflète celles-ci en fonction de l'état de l'attribut. Pour en savoir plus, consultez la section Éviter l'entraide.

    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() est appelé lorsque l'un des attributs du tableau observeAttributes est modifié. C'est un bon endroit pour gérer les effets secondaires, comme la définition d'attributs 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);

L'attribut tabindex ne permet pas de supprimer complètement la sélection d'un élément. Vous pouvez toujours sélectionner les éléments comportant tabindex=-1 avec la souris ou en appelant focus(). Pour vous assurer qu'un élément est désactivé et qu'il n'est pas sélectionnable, supprimez l'attribut tabindex.

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

Si cet élément est actuellement sélectionné, annulez-le en appelant la méthode HTMLElement.blur().

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

    _onKeyUp(event) {

Ils ne gèrent pas les raccourcis de modificateur généralement utilisés par les technologies d’assistance.

      if (event.altKey)
        return;

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

Toute autre pression de touche est ignorée et renvoyée au navigateur.

        default:
          return;
      }
    }

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

_toggleChecked() appelle le setter coché et inverse son état. Comme _toggleChecked() n'est causé que par une action de l'utilisateur, il envoie également un événement de modification. Cet événement s'affiche dans des bulles pour imiter le comportement natif de <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);
})();