Resumo
Um <howto-checkbox>
representa uma opção booleana em um formulário. O tipo mais comum
de caixa de seleção é um tipo duplo que permite ao usuário alternar entre duas
selecionadas e desmarcadas.
O elemento tenta aplicar automaticamente os atributos role="checkbox"
e
tabindex="0"
quando é criado. O atributo role
ajuda na assistência
a tecnologia, como um leitor de tela, informam ao usuário que tipo de controle é esse.
O atributo tabindex
ativa o elemento na ordem de tabulação, tornando-o teclado
focalizáveis e operáveis. Para saber mais sobre esses dois assuntos, confira
O que ARIA pode fazer? e Como usar tabindex.
Quando a caixa de seleção é marcada, ele adiciona um atributo booleano checked
e define
uma propriedade checked
correspondente para true
. Além disso, o elemento define uma
aria-checked
a "true"
ou "false"
, dependendo do
estado. Clicar na caixa de seleção com o mouse ou a barra de espaço ativa
estados marcados.
A caixa de seleção também oferece suporte ao estado disabled
. Se a propriedade disabled
for definido como verdadeiro ou o atributo disabled
for aplicado, a caixa de seleção definirá
aria-disabled="true"
, remove o atributo tabindex
e retorna o foco
ao documento se a caixa de seleção for o activeElement
atual.
A caixa de seleção é pareada com um elemento howto-label
para garantir que ela tenha um
nome acessível.
Referência
- Instruções: componentes no GitHub (link em inglês)
- Padrão de caixa de seleção nas práticas de criação do ARIA 1.1
- O que ARIA pode fazer?
- Como usar o tabindex
Demonstração
Veja a demonstração ao vivo no GitHub
Exemplo de uso
<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>
Código
(function() {
Defina códigos de tecla para ajudar a lidar com eventos de teclado.
const KEYCODE = {
SPACE: 32,
};
A clonagem do conteúdo de um elemento <template>
é mais eficiente do que usar innerHTML, porque evita custos adicionais de análise de 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'];
}
O construtor do elemento é executado sempre que uma nova instância é criada. As instâncias são criadas analisando HTML, chamando document.createElement('howto-checkbox') ou chamando novo HowToCheckbox() O construtor é um bom lugar para criar o shadow DOM, embora você deva evitar tocar em qualquer atributo ou filho do light DOM, porque talvez eles ainda não estejam disponíveis.
constructor() {
super();
this.attachShadow({mode: 'open'});
this.shadowRoot.appendChild(template.content.cloneNode(true));
}
connectedCallback()
é acionado quando o elemento é inserido no DOM. É um bom lugar para definir o role
e o tabindex
iniciais, o estado interno e os listeners de eventos de instalação.
connectedCallback() {
if (!this.hasAttribute('role'))
this.setAttribute('role', 'checkbox');
if (!this.hasAttribute('tabindex'))
this.setAttribute('tabindex', 0);
Um usuário pode definir uma propriedade em uma instância de um elemento antes que seu protótipo seja conectado a essa classe. O método _upgradeProperty()
verifica todas as propriedades da instância e as executa com os setters de classe adequados. Consulte a seção de propriedades lentas para saber mais.
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()
é acionado quando o elemento é removido do DOM. Ele é um bom lugar para fazer tarefas de limpeza, como liberar referências e remover listeners de eventos.
disconnectedCallback() {
this.removeEventListener('keyup', this._onKeyUp);
this.removeEventListener('click', this._onClick);
}
As propriedades e os atributos correspondentes precisam ser iguais. O setter da propriedade para verificado lida com valores verdadeiros/falsos e reflete esses valores para o estado do atributo. Consulte a seção evitar reengajamento para mais detalhes.
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()
é chamado quando qualquer um dos atributos na matriz observáveis é modificado. É um bom lugar para lidar com efeitos colaterais, como a definição de atributos 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);
O atributo tabindex
não fornece uma maneira de remover totalmente a capacidade de foco de um elemento. Elementos com tabindex=-1
ainda podem ser focados com um mouse ou chamando focus()
. Para garantir que um elemento seja desativado e não possa ser focado, remova o atributo tabindex
.
if (hasValue) {
this.removeAttribute('tabindex');
Se o foco estiver nesse elemento, remova o foco chamando o método HTMLElement.blur()
this.blur();
} else {
this.setAttribute('tabindex', '0');
}
break;
}
}
_onKeyUp(event) {
Não processam atalhos modificadores normalmente usados por tecnologia adaptativa.
if (event.altKey)
return;
switch (event.keyCode) {
case KEYCODE.SPACE:
event.preventDefault();
this._toggleChecked();
break;
Qualquer outro pressionamento de tecla é ignorado e retornado ao navegador.
default:
return;
}
}
_onClick(event) {
this._toggleChecked();
}
_toggleChecked()
chama o setter marcado e inverte o estado. Como _toggleChecked()
só é causado por uma ação do usuário, ele também vai enviar um evento de mudança. Esse evento aparece em forma de balão para imitar o comportamento nativo 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);
})();