摘要
<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
元素配對,確保其具有無障礙名稱。
參考資料
示範
使用案例:
<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>
元素複製內容的效能比使用 innerHTML 更高,因為這樣可避免額外的 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'];
}
每次建立新例項時,系統都會執行元素的建構函式。您可以透過剖析 HTML、呼叫 document.createElement('howto-checkbox') 或呼叫 new HowToCheckbox() 來建立例項。建構函式是建立陰影 DOM 的好地方,但請避免觸碰任何屬性或 light DOM 子項,因為這些項目可能尚未可用。
constructor() {
super();
this.attachShadow({mode: 'open'});
this.shadowRoot.appendChild(template.content.cloneNode(true));
}
當元素插入 DOM 時,connectedCallback()
就會觸發。這是設定初始 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;
}
}
當元素從 DOM 中移除時,disconnectedCallback()
就會觸發。這是執行清理工作 (例如釋放參照和移除事件監聽器) 的理想位置。
disconnectedCallback() {
this.removeEventListener('keyup', this._onKeyUp);
this.removeEventListener('click', this._onClick);
}
房源和對應的屬性應互相對應。checked 的屬性設定器會處理真值/假值,並將這些值反映至屬性的狀態。詳情請參閱「避免重入」一節。
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');
}
當 observedAttributes 陣列中的任何屬性變更時,系統會呼叫 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);
})();