Thành phần trong hướng dẫn – hộp đánh dấu cách làm

Tóm tắt

<howto-checkbox> đại diện cho một tuỳ chọn boolean trong biểu mẫu. Loại phổ biến nhất hộp đánh dấu là loại kép cho phép người dùng chuyển đổi giữa lựa chọn -- đã đánh dấu và bỏ đánh dấu.

Phần tử này cố gắng tự áp dụng các thuộc tính role="checkbox"tabindex="0" khi được tạo lần đầu. Thuộc tính role giúp hỗ trợ như trình đọc màn hình cho người dùng biết đây là loại kiểm soát nào. Thuộc tính tabindex chọn phần tử này trong thứ tự thẻ, giúp phần tử này trở thành bàn phím có thể làm tâm điểm và có thể hoạt động. Để tìm hiểu thêm về hai chủ đề này, hãy xem ARIA có thể làm gì?Sử dụng chỉ mục thẻ.

Khi bạn đánh dấu vào hộp này, thao tác này sẽ thêm thuộc tính boolean checked và đặt một thuộc tính checked tương ứng với true. Ngoài ra, phần tử này còn đặt một thuộc tính aria-checked cho "true" hoặc "false", tuỳ thuộc vào trạng thái. Nhấp vào hộp đánh dấu bằng chuột hoặc thanh dấu cách, sẽ bật/tắt các tuỳ chọn này trạng thái đã đánh dấu.

Hộp đánh dấu này cũng hỗ trợ trạng thái disabled. Nếu thuộc tính disabled được đặt thành true hoặc áp dụng thuộc tính disabled, hộp đánh dấu sẽ được đặt aria-disabled="true", xoá thuộc tính tabindex và trả về tiêu điểm vào tài liệu nếu hộp đánh dấu là activeElement hiện tại.

Hộp đánh dấu này được ghép nối với phần tử howto-label để đảm bảo phần tử này có tên dễ tiếp cận.

Tài liệu tham khảo

Bản minh hoạ

Xem bản minh hoạ trực tiếp trên GitHub

Ví dụ về cách sử dụng

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

Xác định các mã phím để giúp xử lý các sự kiện trên bàn phím.

  const KEYCODE = {
    SPACE: 32,
  };

Việc sao chép nội dung từ phần tử <template> hiệu quả hơn so với sử dụng innerHTML vì nó giúp tránh phát sinh thêm chi phí phân tích cú pháp 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'];
    }

Hàm khởi tạo của phần tử sẽ chạy mỗi khi có một thực thể mới được tạo. Các đối tượng được tạo bằng cách phân tích cú pháp HTML, gọi document.createElement('howto-checkbox') hoặc gọi HowToCheckbox(); mới Hàm dựng là nơi thích hợp để tạo DOM bóng, mặc dù bạn nên tránh chạm vào bất kỳ thuộc tính nào hoặc phần tử con DOM sáng vì các thuộc tính này có thể chưa có sẵn.

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

connectedCallback() kích hoạt khi phần tử được chèn vào DOM. Bạn nên thiết lập role, tabindex ban đầu, trạng thái nội bộ và trình nghe sự kiện cài đặt.

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

Người dùng có thể đặt thuộc tính trên một thực thể của một phần tử, trước khi nguyên mẫu của phần tử đó được kết nối với lớp này. Phương thức _upgradeProperty() sẽ kiểm tra mọi thuộc tính thực thể và chạy các thuộc tính đó thông qua phương thức setter lớp thích hợp. Hãy xem phần thuộc tính từng phần để biết thêm thông tin.

      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() kích hoạt khi phần tử bị xoá khỏi DOM. Đây là nơi thích hợp để dọn dẹp các công việc như phát hành tệp đối chiếu và xoá trình nghe sự kiện.

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

Các thuộc tính và các thuộc tính tương ứng phải đồng bộ hoá hai chiều. Phương thức setter thuộc tính cho mục đã kiểm tra sẽ xử lý các giá trị đúng/sai và phản ánh các giá trị đó cho trạng thái của thuộc tính. Hãy xem phần tránh tỷ lệ lặp lại để biết thêm chi tiết.

    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() được gọi khi bất kỳ thuộc tính nào trong mảngObserveAttributes thay đổi. Đây là một nơi phù hợp để xử lý các tác dụng phụ, chẳng hạn như đặt các thuộc tính 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);

Thuộc tính tabindex không cung cấp cách thức để xoá hoàn toàn khả năng lấy tiêu điểm khỏi một phần tử. Bạn vẫn có thể lấy tiêu điểm các phần tử có tabindex=-1 bằng chuột hoặc bằng cách gọi focus(). Để đảm bảo rằng một phần tử bị tắt và không thể lấy tiêu điểm, hãy xoá thuộc tính tabindex.

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

Nếu tiêu điểm đang nằm trên phần tử này, thì hãy huỷ lấy tiêu điểm bằng cách gọi phương thức HTMLElement.blur()

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

    _onKeyUp(event) {

Không xử lý các phím tắt thường dùng của công nghệ hỗ trợ.

      if (event.altKey)
        return;

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

Mọi thao tác nhấn phím khác sẽ bị bỏ qua và chuyển trở lại trình duyệt.

        default:
          return;
      }
    }

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

_toggleChecked() gọi phương thức setter đã kiểm tra và lật trạng thái của phương thức này. Vì _toggleChecked() chỉ do hành động của người dùng gây ra, nên mã này cũng sẽ gửi một sự kiện thay đổi. Sự kiện này tạo bong bóng trò chuyện để bắt chước hành vi gốc của <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);
})();