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> 요소에서 콘텐츠를 클론하면 추가 HTML 파싱 비용을 피할 수 있으므로 innerHTML을 사용하는 것보다 성능이 더 우수합니다.

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

새 인스턴스가 생성될 때마다 이 요소의 생성자가 실행됩니다. 인스턴스는 HTML을 파싱하거나, document.createElement('howto-checkbox')를 호출하거나, 새 HowToCheckbox()를 호출하여 만듭니다. 생성자는 Shadow DOM을 만들기에 좋은 장소이지만 속성 또는 Light 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() 메서드는 인스턴스 속성을 확인하고 적절한 클래스 setter를 통해 실행합니다. 자세한 내용은 지연 속성 섹션을 참고하세요.

      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는 진실/거짓 값을 처리하고 이 값을 속성의 상태에 반영합니다. 자세한 내용은 재진입 방지 섹션을 참고하세요.

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

현재 포커스가 이 요소에 있으면 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()는 선택된 setter를 호출하고 상태를 전환합니다. _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);
})();