Tóm tắt
<howto-checkbox>
đại diện cho một tuỳ chọn boolean trong biểu mẫu. Loại hộp đánh dấu phổ biến nhất là loại kép, cho phép người dùng bật/tắt giữa hai lựa chọn – đã đánh dấu và chưa đánh dấu.
Phần tử này cố gắng tự áp dụng các thuộc tính role="checkbox"
và tabindex="0"
khi được tạo lần đầu tiên. Thuộc tính role
giúp công nghệ hỗ trợ như trình đọc màn hình cho người dùng biết đây là loại thành phần điều khiển nào. Thuộc tính tabindex
chọn phần tử này vào thứ tự thẻ, giúp phần tử này có thể lấy tiêu điểm và thao tác được bằng bàn phím. Để tìm hiểu thêm về hai chủ đề này, hãy xem bài viết ARIA có thể làm gì? và Sử dụng tabindex.
Khi bạn đánh dấu vào hộp đánh dấu, hộp đánh dấu này sẽ thêm một thuộc tính boolean checked
và đặt thuộc tính checked
tương ứng thành true
. Ngoài ra, phần tử này đặt thuộc tính aria-checked
thành "true"
hoặc "false"
, tuỳ thuộc vào trạng thái của phần tử. Nhấp vào hộp đánh dấu bằng chuột hoặc thanh dấu cách để bật/tắt các trạng thái đã đánh dấu này.
Hộp đánh dấu cũng hỗ trợ trạng thái disabled
. Nếu thuộc tính disabled
được đặt thành true hoặc thuộc tính disabled
được áp dụng, hộp đánh dấu sẽ đặ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 được ghép nối với phần tử howto-label
để đảm bảo phần tử đó có tên dễ đọc.
Tài liệu tham khảo
- Hướng dẫn: Thành phần trên GitHub
- Mẫu hộp kiểm trong phương pháp ghi nhận quyền tác giả ARIA 1.1
- ARIA có thể làm gì?
- Sử dụng tabindex
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>
Mã
(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ử được chạy bất cứ khi nào 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 new HowToCheckbox(); Hàm tạo là nơi phù hợp để tạo DOM bóng, tuy nhiên bạn nên tránh chạm vào bất kỳ thuộc tính nào hoặc các thành phần con DOM sáng vì chúng 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. Đây là nơi thích hợp để đặt role
, tabindex
ban đầu, trạng thái nội bộ và cài đặt trình nghe sự kiện.
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 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ải lười để biết thêm thông tin chi tiết.
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 phù hợp để dọn dẹp công việc như phát hành tệp tham 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à thuộc tính tương ứng phải phản ánh lẫn nhau. Phương thức setter của thuộc tính đã đánh dấu xử lý các giá trị đúng/sai và phản ánh các giá trị đó vào trạng thái của thuộc tính. Hãy xem phần tránh tái nhập để 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 để 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ể đặt tiêu điểm vào các phần tử có tabindex=-1
bằng chuột hoặc bằng cách gọi focus()
. Để đảm bảo một phần tử bị vô hiệu hoá 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 hiện đang nằm trên phần tử này, hãy bỏ 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 đều bị bỏ qua và được chuyển 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 _toggleChecked()
cũng sẽ gửi một sự kiện thay đổi. Sự kiện này sẽ nổi lên để mô phỏng 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);
})();