- Aprender
- Computação e TI
- Programação
- Programação em JavaScript
- Eventos e páginas interativas
Eventos e páginas interativas
BásicoAula 6 de 9 do curso Curso de Programação em JavaScript
O que você vai aprender
- Faça uma página reagir quando algo acontecer: registre ouvintes em elementos, leia o que o objeto de evento informa e decida por quanto tempo cada ouvinte permanece ativo.
- Registrar um ouvinte de evento em um elementoUse
addEventListenerpara executar uma função sempre que ocorrer um clique, pressionamento de tecla, ação de formulário ou evento de página em um elemento. - Ler as informações do objeto de eventoUse o objeto de evento recebido por um ouvinte para descobrir o que aconteceu e onde: seu tipo, alvo, alvo atual e detalhes específicos do evento.
- Remover ouvintes e controlar sua execuçãoPare de ouvir quando um ouvinte não for mais necessário, use as opções
once,signalepassivee dispare seus próprios eventos personalizados.
- Registrar um ouvinte de evento em um elemento
- Explique como um evento desce até seu alvo e volta a subir pela página, cancele o comportamento nativo do navegador quando substituí-lo e use o borbulhamento para tratar eventos de vários elementos com um ouvinte.
- Explicar o borbulhamento e a capturaDescreva as três fases da propagação de eventos e preveja a ordem em que ouvintes em elementos aninhados são executados.
- Impedir ações padrão e interromper a propagaçãoCancele a resposta nativa do navegador a um evento quando seu código a substituir e interrompa a propagação de um evento apenas quando isso for realmente necessário.
- Tratar eventos de vários elementos com delegação de eventosUse um ouvinte em um ancestral comum e
event.target.closest()para tratar eventos de vários elementos filhos, incluindo elementos adicionados posteriormente.
- Explicar o borbulhamento e a captura
- Reaja ao pressionamento de teclas, mudanças de foco, cliques, toques e entradas de caneta e construa uma interação de arraste que funcione com todo tipo de ponteiro.
- Responder ao pressionamento de teclas e às mudanças de focoUse
keydown,keyupe os eventos de foco, escolha entreevent.keyeevent.codee crie atalhos de teclado que não atrapalhem o usuário. - Tratar cliques, toques e entradas de caneta com eventos de ponteiroUse
clickpara ativação e eventos de ponteiro para pressionar, mover e soltar com mouse, dedo ou caneta; leia coordenadas e botões; e escolha os eventos adequados para a passagem do ponteiro. - Construir uma interação de arraste com eventos de ponteiroCombine
pointerdown,pointermoveepointerupcom captura de ponteiro para mover um elemento, fazê-lo funcionar em telas sensíveis ao toque e oferecer uma forma de fazer o mesmo sem arrastar.
- Responder ao pressionamento de teclas e às mudanças de foco
- Leia o que os usuários inserem enquanto digitam, trate o envio de formulários em JavaScript e verifique as entradas com os recursos de validação do navegador e suas próprias regras, com mensagens de erro claras.
- Ler campos de formulário e responder à digitação dos usuáriosLeia campos de texto, números, caixas de seleção, botões de opção e listas de seleção, use os eventos
inputechangenos momentos adequados e colete um formulário inteiro comFormData. - Tratar o envio de formulários em JavaScriptOuça o evento
submitdo formulário, impeça o recarregamento da página, leia os dados enviados e saiba o que aciona um envio. - Usar a validação nativa do navegadorDescreva regras com atributos HTML, leia o resultado no objeto
validity, verifique campos por meio de código e estilize campos inválidos no momento adequado. - Escrever validação personalizada com mensagens de erro clarasAdicione regras que os atributos HTML não conseguem expressar, escreva mensagens úteis, mostre-as no momento adequado e associe-as aos seus campos.
- Ler campos de formulário e responder à digitação dos usuários
- Crie elementos interativos que funcionem com mouse, teclado e leitor de tela: prefira HTML nativo, adicione papéis, suporte ao teclado e estado ao construir seus próprios controles e mantenha o foco onde os usuários esperam.
- Preferir elementos nativos e permitir que controles personalizados recebam focoSaiba o que os elementos interativos nativos oferecem automaticamente e o que um controle personalizado construído com um
<div>ou<span>precisa acrescentar: um papel, capacidade de receber foco, um nome acessível e um indicador de foco visível. - Adicionar suporte ao teclado e estado aos controles personalizadosFaça botões personalizados, botões de alternância, controles de expansão e recolhimento, caixas de seleção e grupos de itens funcionarem pelo teclado e mantenha seu estado ARIA sincronizado com o que os usuários veem.
- Manter o foco onde os usuários esperam quando a página mudarMova o foco intencionalmente quando o conteúdo for aberto, fechado ou removido ou mostrar erros, e nunca o mova de forma inesperada enquanto os usuários estiverem digitando ou escolhendo.
- Preferir elementos nativos e permitir que controles personalizados recebam foco
Guia de estudo de Eventos e páginas interativas
1Responder a eventos com ouvintes de eventos
Páginas web respondem por meio de funções registradas para eventos. A configuração escolhe o elemento e o tipo de evento; cada ocorrência posterior traz um objeto de evento que descreve o que aconteceu. Ler esse objeto permite que um ouvinte atue no controle correto, mesmo quando um clique atinge um ícone dentro dele. Referências e opções de ouvintes permitem que um recurso mantenha suas respostas ativas exatamente pelo tempo necessário, e eventos personalizados permitem que uma parte da página notifique outra.
1.1 Ouvintes transformam ações em código
Uma página pode esperar que uma pessoa aja. Um evento informa algo que aconteceu, e um ouvinte de evento é a função chamada em resposta. Registrar um ouvinte configura essa resposta; não executa a ação imediatamente.
Adicione a função ao elemento
Selecione um elemento que já exista, escolha o nome exato do evento e passe uma função:
const button = document.querySelector("#like");
let likes = 0;
button.addEventListener("click", function () {
likes = likes + 1;
button.textContent = "Likes: " + likes;
});
A seleção, a configuração da variável e o registro são executados quando o script é executado. Cada clique posterior executa o corpo da função. Após três cliques, o botão mostra “Likes: 3”. Manter likes fora da função preserva o total entre cliques; colocar let likes = 0 dentro dela o reiniciaria a cada vez.
Passe uma referência à função, como showMenu, sem parênteses. Em button.addEventListener("click", showMenu()), o JavaScript chama showMenu imediatamente e passa seu valor de retorno como ouvinte. Para fornecer um argumento mais tarde, envolva a chamada: button.addEventListener("click", () => showMenu("main"));.
Escolha um evento que realmente ocorra
Nomes são cadeias de caracteres que diferenciam maiúsculas de minúsculas e não têm o prefixo “on”. Use "click", não "Click" ou "onclick". Um nome inventado é aceito sem erro, mas o navegador não o emitirá para um clique. Famílias comuns incluem eventos de ponteiro, como pointerdown e pointerup, eventos de teclado, como keydown, eventos de formulário, como input e submit, e eventos de foco, como focusin. DOMContentLoaded tem sua própria grafia exata de maiúsculas e minúsculas.
Configure o elemento certo no momento certo
Um script no cabeçalho pode ser executado antes de um botão existir. Nesse caso, a seleção retorna null, e chamar addEventListener nele lança um erro. Carregue um script externo com defer, coloque-o após a marcação ou registre a configuração no evento DOMContentLoaded do documento. Um elemento criado posteriormente precisa de configuração após a criação ou de um ouvinte em um ancestral.
querySelectorAll retorna uma NodeList, não um único alvo de evento. Registre ouvintes em seus elementos:
document.querySelectorAll(".tab").forEach((tab) => {
tab.addEventListener("click", selectTab);
});
Permita que ouvintes coexistam
| Método | O que o registro faz |
|---|---|
addEventListener("click", fn) | Adiciona um ouvinte junto aos demais e aceita opções. |
button.onclick = fn | Substitui o único tratador da propriedade; atribua null para removê-lo. |
Um atributo HTML onclick | Coloca JavaScript dentro da marcação. |
Ouvintes com a mesma configuração de captura são executados na ordem de registro. No alvo do evento, ouvintes de captura são executados antes dos ouvintes que não usam captura. Adicionar novamente o mesmo objeto de função com o mesmo tipo e configuração de captura não adiciona uma segunda cópia.
Principais conclusões
- Passe a própria função; use uma função envoltória quando a chamada posterior precisar de argumentos.
- Nomes de eventos exigem grafia exata, incluindo maiúsculas e minúsculas.
- Um elemento deve existir quando seu ouvinte é registrado.
- Percorra uma NodeList para registrar ouvintes em seus elementos.
addEventListenerpermite vários ouvintes; uma propriedade de tratamento armazena um.
1.2 Identifique onde um evento começou
O navegador passa um objeto de evento ao seu ouvinte. Suas propriedades explicam o evento que acionou essa chamada específica. Nomeie o parâmetro event ou e; a escolha é sua.
button.addEventListener("click", (event) => {
console.log(event.type);
console.log(event.currentTarget);
});
Isso registra "click" e o botão durante a chamada do ouvinte. Registrar o objeto inteiro pode ajudar você a descobrir suas outras propriedades.
Distinga a origem do ouvinte
Imagine um botão que contém uma imagem:
<button id="save" data-id="7">
<img src="disk.svg" alt=""> Save
</button>
| Propriedade | Significado | Quando a imagem é clicada |
|---|---|---|
event.target | Onde o evento começou | A imagem |
event.currentTarget | Onde o ouvinte em execução foi registrado | O botão, para um ouvinte no botão |
Clicar no texto “Save” também torna o botão o alvo. O texto é parte do botão; ele não se torna um elemento alvo separado.
Essa diferença importa ao ler dados. A imagem não tem o data-id do botão, então event.target.dataset.id é undefined em um clique na imagem. Um ouvinte registrado diretamente no botão pode ler event.currentTarget.dataset.id. Um ouvinte em um contêiner maior pode encontrar o botão desejado com event.target.closest("button") e verificar se há uma correspondência.
Leia os detalhes deste evento
Todo evento tem um type, um target e um currentTarget. O booleano bubbles informa se ele volta a subir pelos ancestrais. cancelable informa se o código pode cancelar sua ação padrão, e defaultPrevented registra se esse cancelamento ocorreu. timeStamp registra o momento de criação do evento em milissegundos em relação à origem de tempo da página.
Diferentes famílias de eventos acrescentam detalhes úteis. Um click moderno fornece um PointerEvent, que herda propriedades de eventos de mouse, como clientX, clientY e button. Um KeyboardEvent acrescenta key, code e indicadores de teclas modificadoras. Um ouvinte de input pode usar event.target.value para ler o novo texto do campo.
Para um ouvinte em uma galeria, event.target.tagName pode ser "IMG" quando uma foto é clicada ou "DIV" quando o espaço entre fotos é clicado. Trate o caso efetivamente informado. Não suponha que todo evento dentro de um contêiner veio do mesmo tipo de filho.
Use o valor enquanto o ouvinte é executado
Em um ouvinte definido com uma função comum, this é igual a event.currentTarget. Uma função de seta obtém this do código ao redor; ela não recebe o elemento como this. Usar event.currentTarget funciona com ambos os estilos de função.
Após o disparo, currentTarget é redefinido para null. Um console pode mostrar esse valor posterior quando você expande um evento registrado anteriormente. Registre event.currentTarget diretamente dentro do ouvinte para inspecionar o elemento durante a chamada.
Principais conclusões
targetidentifica a origem do evento;currentTargetidentifica o elemento do ouvinte em execução.- Leia os dados do elemento que realmente os possui.
- Famílias de eventos acrescentam propriedades como coordenadas, valores de teclas e modificadores.
currentTargetestá disponível durante a execução do ouvinte e depois se tornanull.
1.3 Controlar a duração de um ouvinte
Um ouvinte deve permanecer ativo enquanto seu recurso precisar dele. Para uma interação temporária com o teclado, mantenha a referência à função para que ela possa ser removida quando a interação for encerrada.
Remova a mesma função
function onKey(event) {
console.log(event.key);
}
document.addEventListener("keydown", onKey);
// When the feature closes:
document.removeEventListener("keydown", onKey);
A remoção compara o tipo de evento, o objeto de função e a configuração de captura. Uma função anônima também pode ser removida quando sua referência é salva, como em const handler = () => save();. Escrever uma nova () => save() na chamada de remoção cria uma função diferente, mesmo que o código pareça idêntico.
Escolha as opções de registro
| Opção | Efeito |
|---|---|
once: true | Remove este ouvinte após sua primeira chamada. |
signal: controller.signal | Remove o ouvinte quando esse controlador é cancelado. |
capture: true | Ouve no caminho de captura. |
passive: true | Indica que o ouvinte não cancelará a ação padrão. |
Um banner de boas-vindas exibido uma única vez pode usar { once: true }. Um recurso com vários ouvintes pode atribuir a eles um único sinal:
const controller = new AbortController();
window.addEventListener("pointermove", moveCursor, {
signal: controller.signal
});
window.addEventListener("keydown", onKey, {
signal: controller.signal
});
// Close the feature:
controller.abort();
O cancelamento remove os dois registros. Um controlador cancelado não pode ser redefinido; crie um novo controlador para um novo período de uso. Para remoção com removeEventListener, repita capture: true se ele tiver sido usado no registro. once, signal e passive não fazem parte dos critérios de correspondência para remoção.
Uma chamada a preventDefault() em um ouvinte passivo é ignorada. Os navegadores costumam tornar passivos por padrão certos ouvintes relacionados à rolagem em alvos no nível do documento, incluindo wheel, touchstart e touchmove; use explicitamente { passive: false } nesses casos quando for necessário cancelar.
Evite acumular funções de retorno
Se cada clique em “Iniciar” adicionar um novo ouvinte anônimo ao tabuleiro, iniciar duas vezes fará um clique no tabuleiro executar duas funções de retorno. Registre o ouvinte do tabuleiro uma vez, reutilize o mesmo objeto de função ou cancele o grupo anterior antes de substituí-lo. Funções de retorno once distintas ainda podem se acumular antes de serem executadas.
Desconectar um elemento preserva seus ouvintes. Eles funcionam se o elemento for reinserido, e dispatchEvent() pode alcançá-los enquanto ele estiver desconectado. O elemento pode ser recolhido pelo coletor de lixo quando ele e seus ouvintes não puderem mais ser alcançados. Um ouvinte em window ou document permanece registrado até ser removido explicitamente.
Dispare um evento personalizado
const event = new CustomEvent("cart:add", {
detail: { id: 42 },
bubbles: true
});
item.dispatchEvent(event);
Os ouvintes leem event.detail.id. O disparo chama os ouvintes imediatamente, antes de retornar. bubbles: true permite que os ancestrais também o recebam. Um evento enviado com dispatchEvent() tem isTrusted igual a false; eventos disparados pelo navegador podem ser confiáveis mesmo quando causados por uma API como focus().
Principais conclusões
- A remoção compara o tipo, a identidade da função e a configuração de captura.
onceencerra um registro após uma chamada; cancelar um sinal encerra seus registros associados.- Um ouvinte passivo não pode cancelar uma ação padrão.
- Novas funções de retorno adicionadas a cada configuração podem se acumular.
- Desconectar um elemento preserva seus ouvintes.
- Eventos personalizados carregam dados em
detaile são disparados de forma síncrona.
2Acompanhar a propagação dos eventos pela página
Elementos aninhados compartilham um caminho de evento. Um clique pode passar por um contêiner antes de chegar ao alvo e visitá-lo novamente durante o borbulhamento, o que faz um ouvinte ser suficiente para uma lista que muda. Acompanhar esse caminho ajuda a explicar por que uma ação em um filho também aciona um pai. Distinga o cancelamento do comportamento do navegador do controle da propagação e use a origem do evento para escolher quais ações um ouvinte compartilhado deve tratar.
2.1 O caminho de um evento por elementos aninhados
Um clique dentro de um botão também pode ser recebido pelos ancestrais do botão. Propagação é o percurso do evento pela árvore do documento. Acompanhar esse percurso explica tanto os ouvintes compartilhados úteis quanto as ações adicionais inesperadas.
Acompanhe três fases
- Captura: o evento percorre o caminho de
windowpara baixo, pelos ancestrais, em direção ao alvo. - Alvo: ouvintes são executados no elemento onde o evento começou.
- Borbulhamento: um evento que borbulha volta a subir pelos ancestrais até
window.
A propriedade eventPhase informa 1 para captura, 2 no alvo e 3 para borbulhamento. Um ouvinte registrado sem opções é executado no alvo ou no caminho de borbulhamento. Um ouvinte com { capture: true } é executado no caminho de descida, inclusive no alvo se estiver registrado nele.
Rastreie um clique concreto
<div id="outer">
<div id="inner">
<button>Go</button>
</div>
</div>
Suponha que o botão e cada div tenham ouvintes de clique comuns que registram seus nomes. Clicar em “Go” registra “button”, “inner” e depois “outer”. Clicar em uma área vazia de inner registra “inner” e depois “outer”; o botão não está no caminho desse evento.
Agora adicione um ouvinte de captura a outer. Um clique no botão registra primeiro “outer (capture)”, depois “button”, “inner” e “outer” pelos ouvintes comuns. O ouvinte de captura vê o clique antes dos descendentes. Após outer, o evento pode continuar por body, html, document e window se houver ouvintes registrados nesses elementos.
É um único evento, não um novo clique criado em cada ancestral. target permanece sendo o botão durante todo o percurso. currentTarget muda conforme cada ouvinte é executado. No ouvinte externo, ler target ainda identifica o botão original.
Verifique quais eventos borbulham
| Borbulha | Não borbulha |
|---|---|
click, input, change, submit | invalid |
keydown, pointerdown | focus, blur |
focusin, focusout | pointerenter, pointerleave |
pointerover, pointerout | mouseenter, mouseleave |
A propriedade bubbles do evento informa se ele tem uma fase de borbulhamento. Eventos que não borbulham ainda podem ser observados por um ouvinte de captura em um ancestral. Para tratar mudanças de foco por meio de um ouvinte comum no contêiner, escolha focusin ou focusout.
Explique um erro de ação aninhada
Um cartão de produto abre uma visão de detalhes quando clicado. Seu botão “Remover” exclui o produto. Clicar nesse botão primeiro o exclui, depois alcança o ouvinte do cartão e abre a visão de detalhes. O caminho do evento explica por que ambas as funções de retorno são executadas.
O cartão pode ignorar cliques de remoção com if (event.target.closest(".remove")) return;. Como alternativa, o botão pode interromper a propagação. Filtrar no cartão preserva o evento para outros ancestrais que possam precisar dele.
Principais conclusões
- A propagação segue as fases de captura, alvo e depois borbulhamento para eventos que borbulham.
- Ouvintes comuns em ancestrais recebem cliques em descendentes no caminho de subida.
targetpermanece fixo enquantocurrentTargetmuda.- Use um evento de foco que borbulha ou captura para observar o foco de um ancestral.
2.2 Cancelar uma ação ou interromper um percurso
Um ouvinte pode mudar o que acontece após um evento ou por onde o evento passa. São decisões distintas. Um evento em um link pode continuar borbulhando mesmo depois que a navegação foi cancelada.
Substitua uma ação do navegador
Uma ação padrão é um comportamento fornecido pelo navegador: seguir um link, enviar um formulário, alternar uma caixa de seleção, inserir texto, rolar com a barra de espaço ou mover o foco com Tab. Use preventDefault() quando seu código substituir essa ação:
help.addEventListener("click", (event) => {
event.preventDefault();
openHelpPanel();
});
Para um link para /help, isso abre o painel sem navegar. O link ainda oferece seu destino quando o JavaScript não é executado.
Somente eventos canceláveis podem ser cancelados. Inspecione event.cancelable; defaultPrevented registra o cancelamento bem-sucedido. input ocorre após a mudança do valor e não é cancelável. Chamar preventDefault() nele não remove o texto inserido. Um evento beforeinput cancelável é o lugar adequado para interceptar uma edição; cancelar um keydown relevante pode impedir o efeito padrão dessa tecla.
Controle a propagação separadamente
| Método | Impede a ação padrão do navegador? | Interrompe ouvintes posteriores? |
|---|---|---|
preventDefault() | Sim, se o evento for cancelável e o ouvinte não for passivo | Não |
stopPropagation() | Não | Impede o percurso para outros elementos; ouvintes neste elemento ainda são executados |
stopImmediatePropagation() | Não | Também interrompe os ouvintes restantes neste elemento |
Um link cujo ouvinte apenas interrompe a propagação ainda segue sua URL. Cancelar apenas a navegação ainda permite que um ouvinte de clique no documento receba o evento. Retornar false de uma função de retorno de addEventListener não faz nenhuma das duas coisas. Uma propriedade ou um atributo em linha de tratamento pode cancelar a ação padrão ao retornar false, mas isso ainda não interrompe a propagação.
Mantenha o comportamento compartilhado funcionando
Um ancestral pode fechar um menu ou contar interações. Interromper a propagação oculta o evento desse código. Muitas vezes, o ancestral pode inspecionar target ou defaultPrevented.
Por exemplo, um menu fecha apenas quando um clique ocorre fora tanto do menu quanto de seu controle de abertura:
document.addEventListener("click", (event) => {
if (!menu.contains(event.target) &&
!menuButton.contains(event.target)) {
menu.hidden = true;
}
});
contains() inclui o próprio elemento e seus descendentes. Verificar o controle de abertura é importante porque o clique que abre o menu também borbulha até o documento. Sem esse segundo teste, o menu recém-aberto fecharia imediatamente.
Cancele apenas uma ação tratada
Em um ouvinte de teclado, verifique a tecla antes de cancelar. Cancelar todas as teclas bloqueia a digitação normal e pode impedir que Tab mova o foco. Em um ouvinte passivo, o cancelamento é ignorado por completo.
Principais conclusões
preventDefault()cancela uma ação padrão que pode ser cancelada, preservando a propagação.stopPropagation()interrompe o percurso, não a ação padrão.stopImmediatePropagation()também pula os ouvintes restantes no elemento atual.inputocorre tarde demais para cancelar uma edição.- Verificações de clique fora devem incluir o pop-up e seu controle de abertura.
- Cancele apenas as ações específicas de teclado que seu código trata.
2.3 Um ouvinte para uma lista que muda
A delegação de eventos coloca um ouvinte em um ancestral estável e usa a origem do evento para escolher uma ação. Ela é útil quando uma lista tem muitos itens ou quando o código cria novos itens após a configuração.
Encontre o controle desejado
Um clique que borbulha chega à lista, independentemente de qual descendente foi clicado. Comece por event.target, encontre o controle correspondente mais próximo e descarte cliques não relacionados:
const list = document.querySelector("#todos");
list.addEventListener("click", (event) => {
const button = event.target.closest(".remove");
if (!button || !list.contains(button)) return;
const item = button.closest("li");
console.log("Removing item " + item.dataset.id);
item.remove();
});
A lista contém itens como <li data-id="7">Buy milk <button class="remove">Remove</button></li>. O ouvinte pertence à lista, então um novo item com a mesma marcação funciona imediatamente após a inserção.
Acompanhe um clique em um ícone pelo código. O alvo é o ícone, closest(".remove") encontra seu botão, e button.closest("li") encontra o item. Seu data-id identifica o item na mensagem. Por fim, remove() altera a página.
Mantenha as duas verificações
| Teste | Por que importa |
|---|---|
!button | Um clique em um espaço vazio da lista ou no texto comum de um item pode não encontrar um botão de remoção. |
!list.contains(button) | closest() pode subir além da lista e encontrar um ancestral externo correspondente. |
Testar apenas event.target.matches(".remove") perde cliques no ícone porque o próprio ícone não corresponde ao seletor. closest() verifica o alvo e sobe por seus ancestrais. Em um ouvinte delegado, currentTarget é a lista; ele não se torna o botão encontrado.
Encaminhe várias ações
Atributos de dados permitem que um ouvinte de barra de ferramentas escolha entre ações conhecidas:
const actions = { bold: makeBold, italic: makeItalic,
clear: clearText };
toolbar.addEventListener("click", (event) => {
const button = event.target.closest("button[data-action]");
if (!button || !toolbar.contains(button)) return;
const action = actions[button.dataset.action];
if (typeof action === "function") action();
});
Um botão com data-action="clear" seleciona clearText. A verificação da função trata uma ação desconhecida sem tentar chamar undefined.
Escolha um evento adequado
A delegação funciona com eventos que borbulham, além de cliques. Um único ouvinte de input no formulário pode passar event.target a uma função de verificação de campo. Para delegar mudanças de foco, use focusin ou focusout; focus e blur comuns não borbulham. Para transições de passagem do ponteiro, use eventos over/out que borbulham quando a delegação for necessária.
Uma chamada a stopPropagation() em um descendente pode impedir que um evento chegue ao contêiner. Quando um ouvinte delegado parecer inativo, verifique o tipo de evento, o alvo, a correspondência com o seletor e se algo abaixo do contêiner interrompeu o percurso.
Principais conclusões
- Registre o ouvinte em um ancestral que permanece mesmo quando seus filhos mudam.
closest()encontra um controle mesmo quando um ícone aninhado é clicado.- Descarte resultados sem correspondência e correspondências fora do contêiner.
- A delegação inclui automaticamente filhos correspondentes adicionados posteriormente.
- Atributos de dados podem encaminhar um ouvinte compartilhado para ações conhecidas.
- A delegação precisa de um evento que alcance o ancestral.
3Tratar entradas de teclado e ponteiro
Uma interação pode ocorrer por teclas, mouse, dedo ou caneta. O evento escolhido determina se você recebe uma ativação, um valor de texto alterado ou o movimento de um ponteiro específico. Referenciais de coordenadas e posições iniciais armazenadas tornam o arraste previsível. O foco e controles alternativos mantêm a página utilizável quando uma pessoa navega pelo teclado ou não consegue realizar um gesto de arraste.
3.1 Teclas, texto e foco
A entrada do teclado vai para o elemento com foco, disponível como document.activeElement, e depois borbulha. Um ouvinte no documento pode, portanto, receber teclas de campos e de outros controles. Esse alcance torna os atalhos de teclado úteis, mas também significa que eles podem interromper a digitação.
Escolha a propriedade pelo seu significado
| Propriedade | Informa | Exemplo |
|---|---|---|
key | O valor produzido, com a disposição do teclado e os modificadores | "a", "A", "Enter", " " |
code | A posição física da tecla | "KeyA", "Enter", "Space" |
Em um teclado AZERTY, a posição identificada como Q em um teclado dos EUA produz key "a" e code "KeyQ". Use key para o significado, como Escape para fechar ou uma letra de atalho. Use code para posições fixas, como teclas de movimento em um jogo. O key da barra de espaço é um único espaço, não "Space".
keydown dispara no pressionamento inicial e pode se repetir enquanto a tecla é mantida pressionada. Chamadas repetidas têm event.repeat === true. keyup dispara quando a tecla é solta. Use keydown em vez do evento obsoleto keypress.
Trate um atalho com precisão
document.addEventListener("keydown", (event) => {
if ((event.ctrlKey || event.metaKey) &&
event.key.toLowerCase() === "s") {
event.preventDefault();
saveDraft();
}
});
Isso trata Ctrl+S e Command+S. A conversão para minúsculas permite também "S". O cancelamento substitui a ação Salvar página do navegador apenas para o atalho correspondente. Outros indicadores de teclas modificadoras incluem shiftKey e altKey.
Para um atalho de uma única letra válido para toda a página, ignore contextos de digitação antes de agir:
if (event.target.matches("input, textarea, select") ||
event.target.isContentEditable) return;
if (event.repeat) return;
A primeira verificação protege a entrada de texto. A segunda faz uma ação ser executada uma vez por pressionamento físico. Não cancele Tab em toda a página; é assim que usuários de teclado avançam.
Ouça o texto depois que ele mudar
Para uma tecla comum que produz um caractere imprimível, a sequência é keydown, beforeinput, input e depois keyup. Ler value em keydown fornece o valor anterior à inserção desse caractere. Use input para uma prévia de texto ou contagem de caracteres. Ele também trata edições como colagem e ditado, que não precisam de um pressionamento de tecla correspondente.
Observe mudanças de foco
focus e blur informam o recebimento e a perda de foco sem borbulhar. focusin e focusout borbulham, então um formulário pode observar seus campos por meio de um único ouvinte. relatedTarget identifica o outro elemento envolvido na mudança ou null quando não há nenhum.
password.addEventListener("focus", () => {
hint.hidden = false;
});
password.addEventListener("blur", () => {
hint.hidden = true;
});
A dica fica disponível quando o campo recebe foco, inclusive por Tab. Chamar element.focus() move o foco por código; a próxima entrada de teclado vai para esse elemento.
Principais conclusões
- Use
keypara o significado produzido ecodepara a posição física. - Proteja atalhos de letra válidos para toda a página contra contextos de digitação e repetições indesejadas.
- Use
inputpara reagir a texto editado, incluindo colagem. focusinefocusoutborbulham;focuseblurnão.relatedTargetinforma o outro destino ou origem do foco.
3.2 Ativação e movimento do ponteiro
Uma pessoa pode usar mouse, dedo, caneta ou teclado. Escolha um evento conforme a interação necessária. click expressa ativação; eventos de ponteiro descrevem pressionamento, movimento e liberação.
Mantenha ações disponíveis pelo teclado
Um botão nativo emite click quando ativado com Enter ou a barra de espaço. Um link com foco faz isso com Enter. Associar uma ação apenas a pointerdown ou mousedown deixa de captar essas ativações pelo teclado. Use click para ações como abrir um painel; use pointerdown quando o trabalho precisar começar no pressionamento, como em um arraste.
Um clique com ponteiro ocorre após pressionar e soltar. Quando essas ações ocorrem em elementos diferentes, o clique pode ter como alvo o ancestral comum mais próximo. dblclick ocorre após dois cliques rápidos; event.detail conta cliques rápidos sucessivos. auxclick trata cliques não primários. contextmenu pode vir de um clique secundário, de um toque longo ou de um comando de menu do teclado.
Use uma única família de eventos de ponteiro
pointerdown, pointermove, pointerup e pointercancel abrangem mouse, toque e caneta. pointerType identifica o dispositivo como "mouse", "touch" ou "pen"; pointerId distingue ponteiros ativos, incluindo dois dedos.
Famílias mais antigas de eventos de mouse e toque ainda existem. Os navegadores podem emitir eventos de mouse para compatibilidade após eventos de ponteiro. Registrar tanto pointerdown quanto mousedown para a mesma ação pode, portanto, executá-la duas vezes.
Escolha coordenadas adequadas à tarefa
| Coordenadas | Origem |
|---|---|
clientX, clientY | Canto superior esquerdo da área de visualização visível |
pageX, pageY | Canto superior esquerdo do documento, incluindo a rolagem |
offsetX, offsetY | Borda do preenchimento do elemento alvo |
Quando o documento é rolado 300 px para baixo e um clique ocorre 100 px abaixo do topo da área de visualização, clientY é 100 e pageY é 400. Mantenha ambas as posições no mesmo referencial de coordenadas ao calcular o movimento. Uma mudança no alvo aninhado também pode mudar a origem das coordenadas de deslocamento.
button informa qual botão mudou em um evento de pressionamento ou liberação: 0 primário, 1 meio, 2 secundário. buttons informa a combinação atualmente pressionada: o primário contribui com 1, o secundário com 2 e o do meio com 4. Assim, buttons === 3 significa que o primário e o secundário estão pressionados; não significa que o botão do meio mudou.
Escolha o comportamento de passagem para um controle inteiro
Eventos over/out borbulham e podem disparar quando o ponteiro cruza os limites dos filhos. Eventos enter/leave não borbulham e descrevem a entrada ou saída do elemento inteiro, incluindo seus filhos. Uma dica pode evitar a oscilação nos limites dos filhos com pointerenter e pointerleave.
Usuários de toque e teclado não passam o ponteiro. Ofereça informações essenciais também por clique ou foco. Para um efeito visual de passagem sem comportamento adicional, o CSS :hover é mais simples que um ouvinte.
Principais conclusões
- Use
clickpara ativação, incluindo a ativação nativa pelo teclado. - Eventos de ponteiro abrangem mouse, toque e caneta em uma única família.
- Escolha coordenadas do mesmo referencial para os cálculos.
buttonidentifica uma mudança;buttonsinforma a combinação pressionada.- Enter/leave tratam um elemento e seus filhos como um todo.
- Informações essenciais precisam estar acessíveis por outros meios além da passagem do ponteiro.
3.3 Um arraste que continua acompanhando o ponteiro
O arraste precisa manter informações entre vários eventos. Armazene as coordenadas iniciais do ponteiro e a posição inicial do elemento. Cada movimento aplica a diferença em relação a esse ponto inicial, para que o elemento não salte colocando seu canto sob o ponteiro.
Calcule o deslocamento
Uma caixa começa em left: 100px; top: 50px. O ponteiro começa nas coordenadas 220, 140 da área de visualização e se move para 260, 125. A mudança horizontal é 40 px; a mudança vertical é −15 px. Somar essas mudanças à posição inicial da caixa resulta em left: 140px; top: 35px.
Use coordenadas de cliente consistentes para as duas leituras do ponteiro. O cálculo geral é a posição inicial do elemento mais o deslocamento do ponteiro. Não some cada deslocamento completo à posição já atualizada da caixa, pois isso contaria novamente o movimento anterior.
Armazene e capture um ponteiro
Para uma caixa sem margem e com posicionamento absoluto dentro de um contêiner com posicionamento relativo:
let start = null;
box.addEventListener("pointerdown", (event) => {
if (start || event.button !== 0) return;
start = { id: event.pointerId, x: event.clientX,
y: event.clientY, left: box.offsetLeft,
top: box.offsetTop };
box.setPointerCapture(event.pointerId);
});
box.addEventListener("pointermove", (event) => {
if (!start || event.pointerId !== start.id) return;
box.style.left = start.left +
(event.clientX - start.x) + "px";
box.style.top = start.top +
(event.clientY - start.y) + "px";
});
function endDrag(event) {
if (start && event.pointerId === start.id) start = null;
}
box.addEventListener("pointerup", endDrag);
box.addEventListener("pointercancel", endDrag);
O ID armazenado descarta movimentos e liberações de um segundo ponteiro. offsetLeft e offsetTop fornecem posições iniciais como números; os estilos precisam do sufixo "px".
A captura de ponteiro mantém os eventos seguintes desse ponteiro direcionados à caixa mesmo quando ele se move para fora dela. O navegador libera a captura após a liberação ou o cancelamento do ponteiro. releasePointerCapture(id) pode liberá-la antes.
Trate a tomada de controle pelo navegador
Um gesto de toque pode se tornar rolagem ou zoom da página, causando pointercancel. Encerre o arraste nesse caso e também em pointerup; caso contrário, um estado desatualizado pode continuar movendo a caixa depois. Aplique touch-action: none à superfície de arraste antes da interação para reservar o gesto. Mantenha essa regra local para que o restante da página possa rolar.
Use user-select: none na superfície arrastável para evitar a seleção de texto. Defina draggable="false" em suas imagens para impedir o comportamento nativo de arraste de imagem.
Ofereça outra forma de mover
Ofereça movimento pelo teclado para um controle com foco e botões de movimento que possam ser clicados ou tocados. Teclas de seta, por si só, não fornecem uma alternativa ao arraste com um único ponteiro. O critério de sucesso 2.5.7 das WCAG 2.2 exige essa alternativa, a menos que o arraste seja essencial ou seja um comportamento não modificado do navegador. Botões nativos de movimento podem servir tanto a usuários de teclado quanto de ponteiro.
A API HTML Drag and Drop, separada desse padrão, usa dragstart, dragover e drop para transferir dados, como arquivos. Sua sequência de eventos difere desta mudança de posição baseada em ponteiro.
Principais conclusões
- Calcule cada posição a partir do início armazenado mais o deslocamento do ponteiro.
- Capture e acompanhe o ID do ponteiro ativo.
- Encerre o arraste tanto em
pointerupquanto empointercancel. - Aplique regras de toque e seleção à superfície de arraste.
- Ofereça acesso pelo teclado e uma alternativa que possa ser clicada ou tocada.
4Tratar formulários e validar as entradas antes do envio
Formulários combinam vários tipos de estado: texto, valores numéricos, escolhas marcadas e nomes repetidos. Leia esses valores no evento adequado e depois trate o envio do formulário como uma única ação. Restrições HTML fornecem regras de validação comuns, enquanto JavaScript acrescenta relações entre campos e feedback mais claro. Um formulário útil preserva o trabalho do usuário, explica cada correção e coloca o foco onde a próxima edição deve ocorrer.
4.1 Ler valores quando eles mudam
Controles de formulário expõem diferentes tipos de estado. O value de um campo de texto é uma cadeia de caracteres; o checked de uma caixa de seleção é um booleano. Ler a propriedade correta é o primeiro passo para cálculos e prévias confiáveis.
Converta números de forma deliberada
Mesmo <input type="number"> tem um value que é uma cadeia de caracteres. Com o texto "3", somar 1 resulta em "31". Number(input.value) + 1 resulta em 4. Uma entrada numérica também expõe valueAsNumber.
Uma entrada vazia precisa de uma decisão própria. Number("") resulta em 0, o que pode fazer uma quantidade ausente parecer um zero real. Verifique primeiro input.value === "" ou use valueAsNumber, que resulta em NaN para um campo numérico vazio. Teste esse resultado com Number.isNaN().
Leia o estado selecionado
| Controle | Propriedade a ler |
|---|---|
| Texto, textarea, lista de seleção | value |
| Entrada numérica usada em cálculos | valueAsNumber ou conversão de um value não vazio |
| Caixa de seleção | checked para o estado ligado/desligado |
Grupo de botões de opção chamado size | form.elements.size.value para o valor selecionado |
O value de uma caixa de seleção é seu texto de envio configurado ou "on" se nenhum for fornecido. Ele não informa se ela está marcada. Uma lista de seleção informa o valor da opção escolhida. Para encontrar o próprio elemento de opção selecionado, consulte input[name="size"]:checked.
Associe o evento à atualização
input informa edições do usuário à medida que ocorrem, incluindo digitação e colagem. change informa uma escolha confirmada: depois que um campo de texto alterado perde o foco ou imediatamente quando uma caixa de seleção, um botão de opção ou uma lista de seleção é alterado.
message.addEventListener("input", () => {
counter.textContent = 280 - message.value.length +
" characters left";
});
Após “Hello”, cinco caracteres foram usados e o contador mostra “275 characters left”. Um ouvinte de keydown seria executado antes de o novo caractere entrar no valor e não detectaria uma colagem por menu. Atribuir message.value por código não dispara input nem change; chame também a função de atualização diretamente.
Colete os campos nomeados de um formulário
form.elements.email.value acessa um controle pelo nome ou ID. Use form.elements["first-name"] para um nome com hífen. O envio usa nomes: um ID sozinho não coloca um campo nos dados.
const data = new FormData(form);
const email = data.get("email");
const topics = data.getAll("topics");
FormData coleta os pares de nome e valor atuais que podem ser incluídos. Caixas de seleção não marcadas, controles desabilitados e campos sem nome são omitidos. get() retorna o primeiro valor de um nome; getAll() preserva valores repetidos, como vários tópicos marcados. Controles de arquivo podem fornecer objetos de arquivo; controles de texto comuns fornecem cadeias de caracteres.
Object.fromEntries(data) é conveniente para nomes distintos, mas mantém apenas o último par para nomes repetidos. Escolha getAll() quando cada seleção importar. Como input borbulha, um ouvinte no formulário pode ler event.target para atualizar qualquer campo alterado.
Principais conclusões
- Converta a cadeia de caracteres de um campo antes de calcular e trate um número vazio separadamente.
- Leia o estado de uma caixa de seleção em
checked. - Use
inputpara edições em tempo real echangepara escolhas confirmadas. FormDatainclui controles nomeados que podem ser coletados, não todos os controles.getAll()preserva nomes repetidos; a conversão para objeto não.- A atribuição de valor por código não emite eventos de edição.
4.2 Tratar o envio do formulário
O envio pertence ao formulário. Ouça seu evento submit para tratar por um único caminho um clique em botão de envio, Enter em um campo de uma linha e uma chamada a requestSubmit() por script.
Substitua a navegação por uma atualização da página
Normalmente, um formulário envia dados para sua URL action e carrega a resposta. Sem uma ação, o destino é a página atual. O método padrão GET coloca os dados na URL. Para atualizar a página atual com JavaScript, cancele essa ação padrão:
const form = document.querySelector("#new-todo");
form.addEventListener("submit", (event) => {
event.preventDefault();
const data = new FormData(form);
const title = data.get("title").trim();
if (title === "") return;
addTodo(title);
form.reset();
});
Esse formulário tem um campo de texto habilitado chamado title. O ouvinte remove espaços das extremidades, ignora um resultado vazio, adiciona um item e redefine os campos para seus valores iniciais. reset() nem sempre significa vazio; um controle com valor inicial retorna a esse valor.
Se o item aparecer brevemente e desaparecer quando a página recarregar, verifique se preventDefault() está faltando. Um ouvinte de clique no botão, por si só, não é o ponto adequado para tratar o formulário, especialmente quando outro caminho o envia.
Saiba o que aciona o envio
O envio implícito por Enter pode clicar no botão de envio padrão habilitado. Enter em um textarea insere uma nova linha. Um botão dentro de um formulário é, por padrão, um botão de envio quando seu tipo é omitido, então um botão “Mostrar senha” deve usar explicitamente type="button".
Quando há vários botões de envio, event.submitter identifica o que foi usado. Ele pode ser null em caminhos sem um botão específico, então o código deve verificar antes de lê-lo:
const button = event.submitter;
const intent = button ? button.value : "save";
const data = button ? new FormData(form, button) :
new FormData(form);
Um botão responsável pelo envio com nome, como name="intent" value="publish", acrescenta esse par quando passado a FormData. Uma chamada a FormData(form) por si só não inclui o par de nome e valor de um botão de envio.
Preserve o caminho de validação
| Caminho de envio | Validação automática | Ouvinte de submit |
|---|---|---|
Ativação de envio ou requestSubmit() | Executada, a menos que seja ignorada por atributos de validação | Executado se a validação permitir |
form.submit() | Ignorada | Ignorado |
A validação nativa é executada antes do evento de envio. Se um campo obrigatório vazio falhar, o navegador informa o problema e não dispara submit. Uma verificação personalizada apenas dentro desse ouvinte não será executada nessa tentativa malsucedida. novalidate no formulário desativa a etapa de validação automática.
Use requestSubmit() quando o código precisar do caminho normal de validação e evento de envio. Ele solicita o envio sem disparar um clique no botão. Passe um botão de envio específico a requestSubmit(button) quando as configurações de envio desse botão devam ser aplicadas.
Principais conclusões
- Trate
submitno formulário, abrangendo envios pelo usuário e por script. - Cancele o envio padrão quando JavaScript substituir a navegação.
- Dê
type="button"a botões de formulário que não enviam. - Use
submitterpara distinguir botões de envio e incluir seus dados quando necessário. - A validação precede
submit;requestSubmit()preserva esse caminho. submit()ignora a validação e o evento de envio.reset()restaura os valores iniciais.
4.3 Deixar o HTML descrever regras de validação
O HTML pode descrever muitas regras de entrada sem um teste personalizado em JavaScript. O navegador verifica essas regras durante o envio e expõe os resultados para seu código. Comece pelas restrições que correspondem aos dados de que você precisa.
Escolha uma restrição
| Atributo | Regra |
|---|---|
required | Um valor ou uma seleção obrigatória deve ser fornecido. |
type="email" ou "url" | O valor deve ter a sintaxe esperada. |
min, max, step | Valores numéricos ou de data devem respeitar limites e incrementos. |
minlength, maxlength | O texto inserido pelo usuário deve cumprir uma regra de comprimento. |
pattern | Um valor de texto não vazio deve corresponder a uma expressão para o valor inteiro. |
Para pattern="[0-9]{5}", “12345” passa; “1234”, “123456” e “12345a” falham. O padrão abrange o valor inteiro, não qualquer parte correspondente. Uma entrada vazia ignora a verificação de padrão, então adicione required quando a omissão não for permitida.
A validação de e-mail verifica a sintaxe, não se uma caixa de entrada existe. Tanto “ann@example.com” quanto “ann@example” passam na regra de e-mail do navegador, enquanto “ann.example.com” não passa. Um padrão adicional pode impor um formato mais específico quando a aplicação exigir.
Leia o motivo da falha
O objeto validity de um campo contém indicadores booleanos. valueMissing, typeMismatch, patternMismatch, tooShort e tooLong descrevem texto ausente ou malformado. rangeUnderflow, rangeOverflow e stepMismatch descrevem restrições numéricas. badInput abrange uma entrada que o navegador não consegue transformar em um valor utilizável, e customError registra uma mensagem de validade personalizada. valid é verdadeiro quando nenhuma restrição falha.
Para um campo numérico com min="0" max="10" step="2", 12 está acima do máximo, 7 não segue o incremento permitido e 6 passa. Mais de um indicador pode ser verdadeiro; não suponha que todo campo inválido tenha exatamente um problema.
Verifique silenciosamente ou informe de forma visível
checkValidity() retorna um booleano e dispara invalid nos controles que falham sem mostrar a mensagem do navegador. reportValidity() verifica e também informa o problema ao usuário. Chame qualquer um deles em um campo ou formulário. validationMessage fornece a mensagem no idioma do navegador. O evento invalid não borbulha.
Restrições de comprimento de texto se aplicam ao texto inserido pelo usuário. Atribuir input.value = "ab" no código não torna tooShort verdadeiro apenas porque minlength é 3. maxlength normalmente também impede que o usuário continue digitando além do limite.
Mostre erros em um momento útil
:invalid se aplica imediatamente, então um campo obrigatório ainda não tocado já corresponde ao seletor. :user-invalid espera uma interação do usuário ou uma tentativa de envio e pode evitar um campo vermelho cedo demais. Use texto para explicar o erro, além de um estilo visual.
novalidate ignora a validação automática de envio para o formulário; formnovalidate a ignora para um botão de envio. Eles não removem restrições nem desativam checkValidity(). As verificações do navegador ajudam os usuários a corrigir erros; o servidor deve validar novamente os dados recebidos, pois uma requisição pode contornar a página.
Principais conclusões
- Use restrições HTML para valores obrigatórios, sintaxe, intervalos, incrementos, comprimento e padrões.
- Um padrão corresponde ao valor não vazio inteiro; use
requiredpara rejeitar uma entrada vazia. - Leia os indicadores de
validitypara identificar por que um campo falhou. checkValidity()verifica;reportValidity()também mostra o resultado.- Atributos que ignoram a validação mantêm a API de validade utilizável.
- Valide também os dados recebidos no servidor.
4.4 Regras personalizadas e mensagens de erro úteis
Uma regra que envolve dois campos precisa de código. Por exemplo, uma confirmação deve corresponder a uma senha, ou uma data final deve vir após uma data inicial. Mantenha a regra separada de como e quando seu erro é exibido.
Defina e limpe um erro personalizado
Uma cadeia de caracteres não vazia passada a setCustomValidity() marca o campo como inválido e define customError. Passar "" limpa essa falha personalizada; restrições de obrigatoriedade, padrão ou outras ainda podem falhar.
function checkMatch() {
const matches = confirm.value === password.value;
confirm.setCustomValidity(matches ? "" :
"The passwords do not match.");
}
password.addEventListener("input", checkMatch);
confirm.addEventListener("input", checkMatch);
Verifique novamente ambas as dependências. Alterar a primeira senha pode invalidar uma confirmação que antes estava correta. Uma mensagem que nunca é limpa mantém o campo inválido depois que a pessoa corrige a divergência. Execute também a verificação inicial quando os campos puderem começar com valores.
Entradas de data fornecem cadeias ISO como "2026-03-10", independentemente do formato local de data exibido. Valores não vazios desses controles podem ser comparados como texto. Se a regra disser que o fim deve ser posterior ao início, rejeite end.value <= start.value, incluindo a igualdade. Trate datas ausentes com required ou com uma verificação separada de valor vazio.
Explique a próxima ação útil
Uma mensagem deve identificar o problema e sua correção. Para um e-mail ausente, diga “Digite seu endereço de e-mail.” Para um texto de e-mail malformado, diga “Digite um endereço de e-mail no formato nome@example.com.” Mantenha o valor inserido para que a pessoa possa editá-lo.
Um campo de nome obrigatório aceita espaços porque eles formam uma cadeia não vazia. Verifique name.value.trim() === "" quando espaços em branco nas extremidades não devem contar. Limpe esse erro personalizado quando aparecer um texto com conteúdo.
Associe mensagens aos controles
<label for="email">Email</label>
<input id="email" name="email" type="email"
required aria-describedby="email-error">
<p id="email-error"></p>
Coloque o erro nesse parágrafo e defina aria-invalid="true" enquanto o campo tiver um erro. Limpe o texto e remova aria-invalid ou defina-o como "false" após a correção. aria-describedby associa a explicação ao campo para a tecnologia assistiva. Use texto; uma borda vermelha sozinha não informa a todos o que falhou.
Escolha o fluxo de validação
Para mensagens inteiramente personalizadas, adicione novalidate e use um ouvinte de submit no formulário. Atualize as regras personalizadas, inspecione a validade dos campos, escreva suas mensagens e impeça o envio quando algum falhar. Mova o foco para o primeiro campo inválido ou para um resumo de erros com links. Se o JavaScript também tratar localmente o envio bem-sucedido, cancele esse envio padrão.
Evite informar que um texto de e-mail está incompleto a cada tecla pressionada no início. Uma sequência útil é mostrar erros após a perda de foco ou o envio e depois verificar novamente um erro já informado em input.
Principais conclusões
- Mensagens personalizadas não vazias tornam um campo inválido;
""limpa apenas esse erro personalizado. - Verifique novamente cada campo do qual uma regra entre campos depende.
- Para uma ordem estrita de datas, rejeite tanto a igualdade quanto um fim anterior.
- Explique uma correção em texto e associe-a ao campo.
- Limpe os erros visíveis e o estado de erro ARIA após a correção.
- O feedback personalizado no envio precisa de um fluxo explícito de validade e foco.
5Construir controles personalizados que todos possam usar
Escolher um elemento nativo fornece grande parte do comportamento de um controle antes de qualquer ouvinte ser escrito. Controles personalizados precisam do mesmo cuidado: um nome claro, acesso pelo teclado, foco visível e estado que corresponda ao que a tecnologia assistiva informa. Grupos precisam de movimento previsível entre itens. Quando o conteúdo abre, fecha ou desaparece, um destino de foco escolhido intencionalmente permite que o usuário continue a interação sem procurar novamente pela página.
5.1 Começar com um controle nativo
Um controle precisa de mais do que uma resposta a clique. As pessoas precisam ser capazes de encontrá-lo, alcançá-lo, entender sua finalidade, operá-lo e ver onde está o foco. O HTML nativo já fornece grande parte desse comportamento.
Escolha o elemento para a tarefa
Use um <button> para uma ação, um <a href> para navegação e um controle de formulário com rótulo para entrada. <details> com <summary> fornece um controle de expansão e recolhimento. Estilize esses elementos com CSS para combinar com a página. Um botão com foco é ativado por Enter ou pela barra de espaço sem um ouvinte de teclado adicional.
Um div com ouvinte de clique não tem a semântica nem o comportamento de teclado nativos do botão. Atribuir-lhe role="button" altera o que a tecnologia assistiva anuncia, mas não permite que ele receba foco nem transforma pressionamentos de teclas em cliques.
| Necessidade | Botão nativo | Div personalizado como botão |
|---|---|---|
| Papel | Integrado | Adicione role="button". |
| Foco pelo teclado | Integrado | Adicione tabindex="0". |
| Enter e barra de espaço | Integrados | Adicione tratamento de teclado. |
| Nome acessível | Geralmente seu texto | Forneça texto significativo ou um nome ARIA. |
| Comportamento desabilitado | disabled | Verifique aria-disabled no código. |
Mantenha a ordem de foco previsível
tabindex="0" adiciona um elemento à sequência normal de Tab na ordem do documento. tabindex="-1" permite foco por código sem adicionar uma parada de Tab. Valores positivos movem elementos para antes da sequência usual e são difíceis de manter; evite-os.
Nenhuma das duas configurações acrescenta comportamento de ativação. Um botão personalizado que pode receber foco ainda precisa de um ouvinte para sua operação pelo teclado. Um papel, uma parada de Tab e uma função de retorno resolvem partes diferentes da interação.
Dê um nome a cada controle
Um leitor de tela usa um nome acessível para distinguir controles. Um botão de texto pode usar seu texto visível. Um campo de texto ou uma lista de seleção precisa de um rótulo associado, como <label for="email">Email</label> combinado com id="email". aria-labelledby pode fazer referência a um texto visível em outro lugar; aria-label pode fornecer um nome quando não houver texto visível adequado.
Um botão de fechar que contém apenas um ícone pode ser <button type="button" aria-label="Close">×</button>. O nome descreve a ação em vez de fazer o usuário deduzir o significado do símbolo.
Mostre e faça cumprir o estado
Mantenha um indicador de foco visível. Se substituir o contorno padrão, forneça um substituto claro com :focus-visible, por exemplo outline: 3px solid #1a73e8. Removê-lo sem substituição oculta a posição do usuário de teclado.
Um botão nativo desabilitado sai da sequência de Tab e não pode ser ativado normalmente. aria-disabled="true" apenas comunica a indisponibilidade. A ação de clique e de teclado de um controle personalizado deve verificar esse estado e retornar sem agir. Centralizar essa verificação na função de ativação mantém os dois caminhos de entrada consistentes.
Principais conclusões
- Use um elemento nativo que corresponda à ação ou entrada.
- Um papel ARIA fornece semântica, não comportamento.
- Use
tabindex="0"para acesso normal por Tab e-1para foco apenas por código. - Forneça um nome acessível e um indicador de foco visível.
- Faça cumprir
aria-disabledno código de ativação.
5.2 Manter o comportamento do teclado e o estado juntos
Um controle personalizado deve produzir o mesmo resultado independentemente de como é ativado. Mantenha a ação em uma função, encaminhe a ela o uso de teclado e ponteiro e atualize o estado lido pela estilização e pela tecnologia assistiva.
Dê ativação por teclado a um botão personalizado
fakeButton.addEventListener("click", activate);
fakeButton.addEventListener("keydown", (event) => {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
activate();
}
});
Isso se aplica a um botão personalizado que pode receber foco. Cancelar a barra de espaço impede a rolagem da página durante a ação. Um botão nativo já produz cliques pela ativação com teclado, então adicionar um segundo caminho por tecla pode executar a ação duas vezes.
Escolha o estado correspondente ao controle
| Controle | Estado |
|---|---|
| Botão de alternância com rótulo fixo | aria-pressed="true" ou "false" |
| Botão que abre um controle de expansão e recolhimento | aria-expanded="true" ou "false" |
| Caixa de seleção ou interruptor personalizado | aria-checked="true" ou "false" |
Um botão de alternância “Silenciar” mantém esse rótulo e altera aria-pressed. Se o design mudar o rótulo da ação para “Ativar som”, omita aria-pressed. Uma caixa de seleção usa role="checkbox" e a barra de espaço para alternar; um interruptor usa role="switch" para o estado ligado/desligado.
Atributos contêm cadeias de caracteres. Compare getAttribute("aria-expanded") com "true", não com o booleano true:
detailsButton.addEventListener("click", () => {
const open = detailsButton.getAttribute(
"aria-expanded") === "true";
detailsButton.setAttribute("aria-expanded", String(!open));
panel.hidden = open;
});
Quando open era verdadeiro, o novo estado é falso e o painel fica oculto. aria-controls pode identificar o painel pelo ID. Os elementos nativos <details> e <summary> fornecem o comportamento de expansão e recolhimento sem esse script.
Use o atributo de estado como fonte para a estilização, como [aria-pressed="true"], para que os estados visível e anunciado mudem juntos. Feche pop-ups com Escape e devolva o foco ao controle de abertura.
Dê ao grupo uma única parada de Tab
Abas, barras de ferramentas e grupos de botões de opção usam Tab para entrar ou sair do grupo e teclas de seta para se mover dentro dele. O tabindex itinerante mantém o item atual em 0 e os outros em -1. Ao mover, defina o item anterior como -1, o novo como 0 e dê foco ao novo item.
Para três itens, mover à direita a partir do índice 2 retorna a 0 com (2 + 1) % 3. Mover à esquerda a partir de 0 usa (0 - 1 + 3) % 3 para chegar a 2. Some o tamanho antes da operação de resto porque -1 % 3 em JavaScript é −1.
Verifique com o teclado: entre no controle, veja o foco, ative-o com as teclas adequadas, mova-se dentro de um grupo e feche um pop-up com Escape. Verifique o estado visível e o estado ARIA a cada mudança.
Principais conclusões
- Encaminhe clique, Enter e barra de espaço de um botão personalizado para uma única ação.
- Botões nativos já oferecem ativação por clique pelo teclado.
- Escolha
aria-pressed,aria-expandedouaria-checkedconforme o estado correspondente. - Compare valores ARIA como cadeias de caracteres e atualize a estilização a partir do mesmo estado.
- O tabindex itinerante dá a um grupo uma parada de Tab e movimento por teclas de seta.
- Escape fecha um pop-up e devolve o foco ao controle de abertura.
5.3 Mover o foco quando a página mudar
O foco determina para onde vai a próxima ação do teclado. Se o controle com foco desaparecer, o usuário pode perder sua posição. Trate o foco como parte da atualização da página, especialmente ao abrir uma caixa de diálogo, excluir um item ou informar um erro.
Abra uma caixa de diálogo e retorne ao controle de abertura
Lembre qual elemento tinha foco antes de abrir. Mova o foco para a nova interação e restaure-o ao fechar:
let opener;
openButton.addEventListener("click", () => {
opener = document.activeElement;
dialog.showModal();
});
dialog.addEventListener("close", () => {
if (opener && opener.isConnected) opener.focus();
});
showModal() em uma caixa de diálogo nativa move o foco para dentro dela e torna o restante da página inerte, de modo que não possa ser clicado nem receber foco enquanto a caixa modal estiver aberta. Escape normalmente causa um evento cancel, seguido pelo fechamento e por um evento close. Um ouvinte de cancel pode impedir o fechamento se a aplicação precisar desse comportamento.
Escolha um local inicial de foco útil na caixa de diálogo, como seu primeiro controle relevante. Para um pop-up personalizado, o código deve mover o foco intencionalmente. Quando o controle de abertura original não existir mais, escolha um controle útil próximo em vez de tentar dar foco a um elemento desconectado.
Permita que um destino útil receba foco
Um título ou resumo de erros normalmente não pode receber foco. Dê a ele tabindex="-1" antes de chamar .focus(). Ele poderá receber o foco intencionalmente sem se tornar uma parada extra durante a navegação normal por Tab. Use 0 apenas quando ele pertencer a essa sequência normal.
Preserve a posição após uma exclusão
Antes de remover um item de lista cujo botão de exclusão tem foco, encontre o próximo destino útil. Dê foco ao botão do próximo item; se não houver próximo item, use o botão do item anterior. Se a lista ficar vazia, dê foco ao seu título com tabindex="-1".
Encontrar o destino antes da remoção evita perder a relação com os itens vizinhos. Não envie o foco para o início do documento após cada exclusão; a pessoa deve poder continuar trabalhando perto da alteração.
Mantenha o foco em uma interação em andamento
Um pop-up pode fechar quando o foco sai de toda a sua área. O evento focusout, que borbulha, fornece o destino:
menu.addEventListener("focusout", (event) => {
if (!menu.contains(event.relatedTarget)) {
menu.hidden = true;
}
});
Mover-se entre dois controles do menu não o fecha porque o destino permanece dentro dele. Um destino null também está fora; contains(null) é falso. Não devolva o foco automaticamente ao controle de abertura quando o usuário tiver se movido intencionalmente para outro controle da página.
Torne as mudanças previsíveis
Após uma validação personalizada de formulário malsucedida, dê foco ao primeiro campo inválido ou a um resumo de erros com links para eles. A validação nativa já dá foco a um campo que falha. Mantenha o texto de erro disponível para que a pessoa saiba o que corrigir.
Não pule para um novo campo enquanto o usuário estiver digitando nem navegue assim que uma lista de seleção mudar. Permita que uma ação clara confirme a alteração. O movimento de foco deve apoiar uma ação deliberada ou a recuperação, não interromper a entrada normal.
Principais conclusões
- Lembre o controle de abertura e restaure o foco em um destino disponível quando uma caixa de diálogo fechar.
- Use
tabindex="-1"para um título ou resumo que recebe foco por código. - Após uma exclusão, dê foco a um item vizinho ou ao título da lista vazia.
- Use
relatedTargetpara distinguir movimentos internos de foco da saída de um pop-up. - Dê foco a um destino de erro após uma validação malsucedida; evite saltos inesperados de foco.
Exercícios resolvidos de Eventos e páginas interativas
1Construir e inspecionar ouvintes de eventos
13 passos
- 1IntroduçãoFazer um botão responder
Um ouvinte conecta um evento a uma função. Construa um pequeno contador escolhendo o botão, registrando sua ação e mantendo o total entre chamadas. Depois, inspecione um
clickaninhado e controle a duração do ouvinte.Observe a diferença entre o código de configuração, que é executado agora, e a função de retorno, que o navegador chama quando o evento escolhido acontece.
- 2ExemploArmazenar o total fora da função de retorno
Selecione o botão existente e armazene sua contagem antes do registro:
const button = document.querySelector("#like"); let likes = 0; button.addEventListener("click", () => { likes = likes + 1; button.textContent = "Likes: " + likes; });Cada chamada soma ao total anterior. Após três cliques, o rótulo é “Likes: 3”. Uma contagem declarada dentro da função de retorno recomeçaria a cada chamada.
- 3DicaPassar a ação sem chamá-la
addEventListener("click", showMenu)passa uma função para uso posterior.showMenu()a chamaria durante a configuração. Quando argumentos forem necessários, use() => showMenu("main").Carregue o script depois que o botão existir, por exemplo com
defer, e mantenha a grafia exata do evento. Um ouvinte de"Click"não recebe eventos"click"comuns. - 4DesafioCorrigir um registro
Um botão deve abrir o painel de dicas a cada ativação. Atualmente, a configuração usa
button.addEventListener("click", openTips()). O que deve mudar e por quê?---reveal---
Passe
openTipssem parênteses. Isso fornece a função de retorno para ser executada em umclickposterior. ChamaropenTips()durante a configuração abre o painel imediatamente e passa seu resultado em vez de preservar a ação para o evento. - 5ExplicaçãoLer as duas propriedades de elemento
Para um botão que contém uma imagem, clicar na imagem faz dela o
target. No ouvinte do botão,currentTargeté o botão.O
targetinforma onde o evento começou; otargetatual informa onde essa função de retorno foi registrada. Essa diferença explica por que ler odatasetde um botão a partir detargetpode falhar quando oclickatinge sua imagem. - 6ExemploLer os dados do botão que os possui
Suponha que
buttontenhadata-id="7"e contenha um ícone:button.addEventListener("click", (event) => { console.log(event.currentTarget.dataset.id); });O resultado é
"7"tanto para umclickno ícone quanto para umclickno fundo do botão. A mesma expressão funciona em um ouvinte definido com função de seta ou função comum. Othisde uma função de seta não identifica automaticamente o botão. - 7DesafioIdentificar o elemento do ouvinte
Uma seção contém um botão com um span. Um ouvinte de
clickestá registrado apenas na seção, e o span é clicado. O quetargetecurrentTargetidentificam durante essa função de retorno?---reveal---
targetidentifica o span onde o evento começou.currentTargetidentifica a seção onde o ouvinte foi registrado. A origem do evento não é substituída pelo ancestral que o recebe. - 8ExplicaçãoManter a referência para remoção
A remoção precisa do mesmo tipo de evento, objeto de função de retorno e configuração de captura:
const handler = () => closePanel(); button.addEventListener("click", handler); button.removeEventListener("click", handler);Uma função anônima pode ser removida quando salva dessa forma. Escrever uma nova
() => closePanel()na chamada de remoção cria outro objeto e não corresponde ao registro anterior. - 9ConexãoEncerrar vários registros juntos
Um recurso temporário pode passar um sinal de
AbortControllera vários registros. Chamarcontroller.abort()remove esses ouvintes, incluindo funções de retorno salvas ou anônimas.Use um novo controlador para outro período de uso. Para uma única resposta,
{ once: true }remove esse registro após sua execução. Nenhuma das duas opções impede que funções de retorno distintas se acumulem antes do fim de sua duração. - 10DesafioDiagnosticar configurações repetidas
Abrir um painel registra um novo ouvinte anônimo de teclado no documento a cada vez. Fechar oculta o painel, mas deixa o ouvinte. Após três aberturas, uma tecla aciona três ações. Como corrigir o ciclo de vida?
---reveal---
Remova os registros quando o painel fechar, por exemplo, cancelando seu sinal compartilhado, e crie um novo controlador ao reabri-lo. Como alternativa, salve uma função de retorno e remova essa mesma referência. Ocultar o painel não remove um ouvinte do documento.
- 11Ideia-chaveDisparar uma notificação imediatamente
Para notificar outro código, crie um
CustomEventcom seus dados emdetail. Definabubbles: truese os ancestrais devam recebê-lo:item.dispatchEvent(new CustomEvent("cart:add", { detail: { id: 42 }, bubbles: true }));Os ouvintes são executados antes de a chamada retornar. O evento disparado tem
isTrustedfalso. É uma notificação enviada pelo seu código, não uma solicitação para que o navegador espere por uma ação do usuário. - 12DesafioCombinar origem e duração
Um tratador de
clickde um botão usa seu própriodata-keye deve agir apenas na primeira ativação. Ele contém um ícone SVG. Qual propriedade e qual opção atendem aos dois requisitos?---reveal---
Leia
event.currentTarget.dataset.keyno ouvinte direto do botão e registre-o com{ once: true }. A propriedade identifica o botão que possui os dados mesmo em umclickno ícone. A opção remove esse registro após sua primeira chamada. - 13RecapitulaçãoVerificar um ouvinte em três pontos
Quando uma interação falhar, inspecione a configuração, os dados da função de retorno e sua duração. Confirme que o elemento existe e que o nome do evento é exato; inspecione
targetecurrentTargetdurante a chamada; depois procure remoções ausentes ou registros repetidos. Essas verificações distinguem um ouvinte que nunca recebe um evento de um que o recebe corretamente, mas age sobre o elemento errado.
2Rastrear eventos e delegar ações
13 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoRastrear um clique por um cartão
- 2ExemploLer o caminho de subida
- 3ExplicaçãoAdicionar o caminho de descida
- 4DesafioRastrear uma árvore menor
- 5ExplicaçãoSubstituir a navegação
- 6Ideia-chaveInterromper o percurso é uma escolha diferente
- 7DesafioManter ativo um observador no pai
- 8ExplicaçãoMover o ouvinte para a lista
- 9ExemploEncontrar e verificar o botão de remoção
- 10DesafioTratar um ícone aninhado
- 11DicaUsar um evento que alcance o contêiner
- 12DesafioManter a abertura e o fechamento separados
- 13RecapitulaçãoEscolher um limite para cada ação
3Tratar teclas e construir um arraste com ponteiro
16 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoEscolher o nível correto de entrada
- 2ExplicaçãoSeparar significado de posição
- 3DicaProteger a digitação e tratar repetições
- 4DesafioEscolher um evento para uma prévia
- 5ConexãoPermitir ativação pelo teclado
- 6ExemploConverter o referencial de coordenadas
- 7DesafioInspecionar um clique após a rolagem
- 8Ideia-chaveEvitar que a passagem do ponteiro seja o único caminho
- 9ExemploCalcular um movimento de arraste
- 10ExplicaçãoArmazenar o início e capturar seu ponteiro
- 11ExemploAplicar o deslocamento a cada movimento
- 12DicaEncerrar mesmo se o navegador assumir o controle
- 13DesafioPrever a continuação de um arraste
- 14ConexãoOferecer movimento sem arrastar
- 15DesafioDiagnosticar duas falhas de arraste
- 16RecapitulaçãoAssociar o evento à tarefa
4Construir uma interação completa de formulário
16 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoAcompanhar um formulário da edição ao envio
- 2ExemploLer um número sem concatenação
- 3ExplicaçãoLer escolhas e nomes repetidos
- 4DesafioPreservar cada tópico selecionado
- 5ExplicaçãoTratar o envio no formulário
- 6ExemploLer, adicionar e redefinir
- 7DesafioManter o caminho normal de envio
- 8ExemploLer falhas de restrições independentes
- 9Ideia-chaveUm padrão não exige um valor
- 10DesafioVerificar um intervalo com incrementos
- 11ExplicaçãoAdicionar uma regra que conecta dois campos
- 12DicaSeparar validade do momento de exibição do erro
- 13DesafioCorrigir uma regra de data desatualizada
- 14ConexãoAssumir a responsabilidade ao ignorar o feedback do navegador
- 15DesafioCompletar o fluxo de recuperação
- 16RecapitulaçãoVerificar caminhos válidos e inválidos
5Construir controles e preservar a posição do teclado
14 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoFazer uma interação funcionar pelo teclado
- 2ExplicaçãoUsar o comportamento que o HTML já fornece
- 3DicaDefinir a capacidade de foco para o caminho desejado
- 4DesafioCorrigir uma ação inacessível
- 5ExemploManter uma única função de ativação
- 6ExplicaçãoExpressar o tipo correto de estado
- 7DesafioInverter corretamente um controle de expansão e recolhimento
- 8ExemploMover uma parada de Tab dentro de um grupo
- 9ConexãoLembrar de onde uma caixa de diálogo veio
- 10ExemploManter interno um movimento de foco
- 11DesafioEscolher o foco após a remoção
- 12DesafioRastrear uma atualização de grupo
- 13DesafioManter um pop-up previsível
- 14RecapitulaçãoTestar a sequência completa do controle
Questões de Eventos e páginas interativas
Conjunto 1: Tratar entradas de teclado, ponteiro e arraste10 Questões de Prática
Questão 1 de 10
Mostrar respostaOcultar resposta
key descreve o valor produzido pela disposição atual do teclado e pelos modificadores, então é adequado para um atalho baseado no significado de uma letra. code descreve a posição física da tecla. Escolher essa posição manteria um controle de jogo no lugar, mas não acompanharia a letra produzida em diferentes disposições.Questão 2 de 10
keydown. Qual mudança resolve os dois sintomas?Mostrar respostaOcultar resposta
input é executado após a edição do campo e inclui colagem sem pressionamento de uma tecla física. Portanto, ele corrige tanto o momento da leitura quanto a edição não detectada. keyup pode ser executado após a digitação, mas uma colagem pelo menu de contexto não exige a liberação de uma tecla, então esse caminho ainda não seria detectado.Questão 3 de 10
Mostrar respostaOcultar resposta
isContentEditable, junto com alvos input, textarea e select. O indicador repeat filtra as chamadas de tecla mantida pressionada para que um novo pressionamento aja uma vez. Cancelar todas as teclas antes de verificar o alvo ainda interromperia a digitação, mesmo quando a ação do painel fosse ignorada.Questão 4 de 10
pointerdown. Qual evento deve tratar a ação?Mostrar respostaOcultar resposta
click tanto para ativação pelo teclado quanto por ponteiro. Tratar esse evento dá à ação um caminho comum. Substituir pointerdown por mousedown manteria a ação ligada ao pressionamento de um dispositivo apontador e ainda deixaria de captar o clique nativo pelo teclado.Questão 5 de 10
clientY 75. Qual é sua coordenada vertical em relação ao documento?Mostrar respostaOcultar resposta
pageY relativo ao documento inclui o deslocamento de rolagem, então a posição é 420 mais 75, ou 495 px. Manter 75 descreveria apenas a posição na área de visualização. Subtrair o deslocamento de rolagem segue a direção errada ao converter coordenadas da área de visualização para coordenadas do documento.Questão 6 de 10
pointerout. Qual evento detecta melhor a saída do cartão inteiro?Mostrar respostaOcultar resposta
pointerleave descreve a saída do cartão como um todo, incluindo seus filhos. pointerout pode disparar nas transições pelos limites dos filhos, explicando por que entrar no indicador oculta a dica. A dica ainda precisa de um caminho acessível, como foco ou clique, para pessoas que não passam o ponteiro.Questão 7 de 10
left: 65px; top: 90px. Seu ponteiro começa em 180, 210 e se move para 155, 245 em coordenadas de cliente. A que posição a caixa deve chegar?Mostrar respostaOcultar resposta
Questão 8 de 10
Mostrar respostaOcultar resposta
Questão 9 de 10
Mostrar respostaOcultar resposta
touch-action: none aplicado localmente informa ao navegador que a superfície de arraste controla esse gesto de toque. Limpar o estado ativo em pointercancel também trata qualquer cancelamento que ainda ocorra. A captura de ponteiro, por si só, encaminha eventos de ponteiro; ela não reserva gestos do navegador nem limpa o estado de arraste da aplicação.Questão 10 de 10
Mostrar respostaOcultar resposta
Conjunto 2: Configurar e inspecionar ouvintes de eventos10 Questões de Prática
Salve esta aula para ver todas as respostas e praticar cada conjunto.
Questão 1 de 10
target e currentTarget durante a execução desse ouvinte?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
showHints apenas quando for ativado. Qual registro consegue isso?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
addEventListener. Qual mudança no carregamento permite que a seleção espere até a análise sintática do HTML?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
const items = document.querySelectorAll(".choice"); items.addEventListener("click", choose);. Todas as opções já existem. Por que isso falha e como a configuração deve mudar?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
button.onclick = first; button.onclick = second;. O que acontece quando o botão é clicado uma vez?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
<button data-code="r8"><span>Run</span></button> lê event.target.dataset.code. Por que ele pode retornar undefined e qual expressão, em um ouvinte direto, lê o código do botão?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
currentTarget: null. Como a pessoa desenvolvedora pode inspecionar o elemento do ouvinte durante a chamada?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
{ capture: true }. O mesmo tipo e a mesma função são passados a removeEventListener, mas nenhum terceiro argumento é fornecido. Por que a função de retorno permanece?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
board.addEventListener("click", () => mark()). Iniciar duas vezes faz um clique no tabuleiro colocar duas marcas. Qual mudança na configuração impede esse acúmulo?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
Um botão sem outros ouvintes recebe estes registros e depois é clicado duas vezes. Quantas vezes log é executada no total?
button.addEventListener("click", log, { once: true });
button.addEventListener("click", log);
button.addEventListener("click", log, true);
button.removeEventListener("click", log);
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Conjunto 3: Rastrear ações aninhadas e delegação10 Questões de Prática
Salve esta aula para ver todas as respostas e praticar cada conjunto.
Questão 1 de 10
event.stopPropagation(). O que acontece com sua navegação normal?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
focus, mas dar foco ao seu input filho não o chama. Como o formulário pode observar o foco em descendentes sem usar captura?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
preventDefault(). Também existe um ouvinte comum de envio em uma seção que o envolve. O que o ouvinte da seção pode observar?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
closest(".action") dentro de um contêiner aninhado em um elemento externo .action. Um clique em um espaço vazio encontra esse elemento externo. Qual verificação mantém o tratamento local?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
closest(".remove") e verificações de pertencimento. Novos botões funcionam, exceto em uma linha cujo ouvinte do ícone chama stopPropagation(). Clicar na área de preenchimento do botão da mesma linha funciona. Por que ocorre essa diferença?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Conjunto 4: Ler dados de formulário e diagnosticar a validação10 Questões de Prática
Salve esta aula para ver todas as respostas e praticar cada conjunto.
Questão 1 de 10
"6". O que input.value + 2 produz?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
topic, com valores "art" e "code" na ordem do documento, uma entrada topic não marcada com valor "music" e uma nota desabilitada com nome. O que new FormData(form).getAll("topic") retorna e por que a conversão para objeto é arriscada?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
form.submit(), e o ouvinte do formulário que impede a navegação nunca é executado. Qual chamada restaura o fluxo esperado e o que acontece antes de esse campo ser corrigido?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
pattern="[A-Z]{3}" e é opcional. Um valor vazio passa nessa restrição de padrão?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
min="2" max="14" step="4". O usuário insere 12. O que falha?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
novalidate e verifica os campos na ordem do documento durante o envio. Sua quantidade é obrigatória e tem min="2" max="14" step="4"; agora contém 10 e uma mensagem personalizada antiga foi limpa com setCustomValidity(""). O campo seguinte é um e-mail obrigatório que está vazio. O que deve bloquear essa tentativa e receber foco?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
end.value < start.value e é executada apenas em edições da data final. Quais duas mudanças corrigem as falhas restantes entre os campos?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Conjunto 5: Construir acesso pelo teclado e preservar o foco10 Questões de Prática
Salve esta aula para ver todas as respostas e praticar cada conjunto.
Questão 1 de 10
role="button" e um ouvinte de clique. O que o próprio papel fornece?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
activate(). Ele tem aria-disabled="true". A função começa com if (button.getAttribute("aria-disabled") === true) return;, mas os dois caminhos ainda executam a ação. Qual correção faz valer o estado desabilitado e permite a ação no estado da cadeia de caracteres “false”?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
aria-expanded="true" e um painel visível. Seu tratador de clique lê o estado expandido anterior. Qual deve ser o próximo estado?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
tabindex="-1". O botão de exclusão com foco no último item remove esse item; depois, o item restante também é excluído. Qual sequência de foco permite continuar usando o teclado?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Travou em uma questão sua? Ajuda com Tarefas
Termos-Chave
A
Ação padrão
Alternativa com um único ponteiro
Ativação
click para uso com ponteiro e teclado nativo.click quando pressionado com a barra de espaço ou com o mouse.Atributo aria-describedby
aria-describedby associa um controle a um texto descritivo listando os IDs dos elementos que contêm esse texto.email-error para que a tecnologia assistiva possa associar o erro a ele.Atributo aria-invalid
aria-invalid comunica que o valor inserido em um controle tem um erro; o código deve atualizá-lo conforme o erro muda.aria-invalid="true" enquanto sua mensagem de correção em linha é exibida.Atributo de dados
data- e armazena informações da aplicação disponíveis por meio do dataset de um elemento.data-action="clear" como button.dataset.action para selecionar uma ação conhecida.Atributo novalidate
novalidate ignora a validação automática de restrições durante o envio, mantendo disponíveis as restrições e os métodos de validade.novalidate para que seu ouvinte de envio possa inspecionar cada campo.Atributo pattern
pattern define uma restrição de expressão regular para o valor inteiro de entradas de texto compatíveis, aplicada apenas quando seu valor não está vazio.required para rejeitar um campo vazio.Atributo tabindex
tabindex controla a participação normal na navegação por Tab e a capacidade de receber foco por código, com 0 na ordem normal e −1 fora dessa sequência.tabindex="-1" para que o código possa lhe dar foco sem adicionar uma parada comum de Tab.B
Borbulhamento
bubbles é verdadeiro.input de um campo filho borbulha até um ouvinte registrado no formulário que o contém.Botão de alternância
aria-pressed para informar o estado atual.aria-pressed muda entre verdadeiro e falso.Botão responsável pelo envio
event.submitter quando um botão específico iniciou esse caminho.C
Caixa de diálogo modal
showModal() abre a caixa de diálogo nativa e move o foco para seu conteúdo.Captura de ponteiro
Coleção FormData
data.getAll("topics") obtém juntas todas as marcadas.Conteúdo de fundo inerte
Controlador de cancelamento
abort() é chamado.Controle de abertura
Controle de expansão e recolhimento
aria-expanded.Controle nativo
Coordenadas da área de visualização
clientX e clientY.Coordenadas do documento
pageX e pageY, incluindo a rolagem.pageY 400.D
Delegação de eventos
Disparo síncrono
dispatchEvent(), antes de esse método retornar o controle ao código que o chamou.dispatchEvent() aparece depois das mensagens de seus ouvintes.E
Estado ARIA
aria-checked do interruptor é atualizado quando sua aparência visível de ligado/desligado muda.Estado de arraste
Estado de validade (ValidityState)
validity de um controle, com indicadores booleanos que identificam restrições não atendidas.stepMismatch mesmo estando entre seu mínimo e máximo.Evento
click permite que a página responda quando uma pessoa ativa o botão de avançar da galeria.Evento change
change informa uma alteração confirmada pelo usuário, como sair de um campo de texto editado ou selecionar um novo estado em uma caixa de seleção ou lista de seleção.change quando o usuário sai dele para o próximo controle.Evento de ponteiro
pointermove pode ler o movimento de um mouse ou de um contato por toque.Evento input
input informa uma edição do usuário após o valor de um controle mudar, incluindo edições de texto feitas sem o pressionamento de uma tecla física.input do campo.Evento keydown
keydown informa o pressionamento de uma tecla antes de seu efeito normal de edição e pode se repetir enquanto a tecla física permanece pressionada.keydown antes de fechar o painel aberto.Evento personalizado
detail e ser configurado para borbulhar até os ancestrais."cart:add" com seu identificador no detail do evento.Evento pointercancel
pointercancel informa que o navegador cancelou a interação do ponteiro, exigindo que um arraste limpe seu estado ativo.pointercancel.Evento submit
submit é um evento cancelável de formulário disparado durante o caminho normal de envio após a validação automática permitir o envio.submit de um formulário trata um envio válido adicionando um item localmente e cancelando a navegação.F
Fase de captura
Fase do alvo
eventPhase 2 enquanto são chamados.Foco
document.activeElement.search.focus() direciona o próximo caractere digitado para o campo de busca.G
Gerenciamento de foco
Grupo composto
I
Identidade do ouvinte
Indicador de foco
:focus-visible mostra qual botão da barra de ferramentas receberá a próxima ação de teclado.Indicador de tecla modificadora
ctrlKey ou metaKey junto com a letra S.M
Máscara de bits dos botões
buttons do evento.buttons 3, combinando os indicadores 1 e 2.Método checkValidity
checkValidity() retorna se as restrições são atendidas e dispara eventos invalid para falhas sem exibir feedback do navegador.Método closest
closest() verifica um elemento e depois seus ancestrais em busca da correspondência mais próxima com um seletor, retornando null se nenhuma existir.closest("button") encontra o botão de ação que o envolve.Método contains
contains() testa se um nó é o próprio elemento ou um de seus descendentes, retornando um resultado booleano.list.contains(button) antes de permitir a ação do botão.Método preventDefault
preventDefault() cancela a ação padrão de um evento cancelável em um ouvinte não passivo sem interromper sua propagação.preventDefault() antes de adicionar um item local sem navegar.Método reportValidity
reportValidity() verifica restrições e também solicita que o navegador exiba feedback de validação para falhas não tratadas.reportValidity() para ajudar o usuário a encontrar um campo que falha.Método requestSubmit
requestSubmit() solicita o envio de um formulário pelo caminho normal de validação e evento de envio sem disparar um clique no botão.form.requestSubmit() para que a validação e o ouvinte de envio do formulário ainda sejam executados.Método stopImmediatePropagation
stopImmediatePropagation() interrompe o percurso do evento e também pula os ouvintes restantes registrados no alvo atual do evento.Método stopPropagation
stopPropagation() impede que um evento percorra outros elementos, mantendo ativos os demais ouvintes no elemento atual.N
Nome acessível
aria-label="Close" para informar o que seu símbolo faz.O
Objeto de evento
key do evento recebido para decidir se fecha um painel.Opção once
once solicita a remoção automática desse registro após sua primeira chamada para o tipo de evento selecionado.once: true para o clique que o dispensa durante esse período de uso.Ouvinte de evento
Ouvinte passivo
preventDefault() em um ouvinte passivo de toque não interrompe a rolagem do navegador.P
Papel ARIA
role="button" ainda precisa de código para ativação e de uma parada de Tab adequada.Propagação
Propriedade cancelable
cancelable é um booleano que indica se um evento permite o cancelamento de sua ação padrão por meio de preventDefault().input informa uma edição já realizada, então sua propriedade cancelable é falsa.Propriedade checked
checked é o estado booleano que indica se uma caixa de seleção ou entrada de botão de opção está marcada, independentemente de seu valor de envio.checkbox.checked para decidir se mostra uma seção opcional.Propriedade code
code identifica uma posição física de tecla independentemente do caractere produzido pela disposição atual do teclado.code quando seus controles de movimento devem permanecer em posições físicas fixas.Propriedade CSS touch-action
touch-action declara quais gestos de toque do navegador são permitidos quando um gesto começa em um elemento.touch-action: none na superfície de arraste reserva o movimento nessa área para o arraste.Propriedade currentTarget
currentTarget identifica o objeto cujo ouvinte está sendo executado e é redefinida para null após o término do disparo do evento.currentTarget permite que o código leia os dados desse botão.Propriedade de tratamento de evento
onclick, que armazena uma função de retorno de evento e a substitui quando recebe uma nova atribuição.button.onclick substitui o tratador anterior dessa propriedade.Propriedade defaultPrevented
defaultPrevented é um booleano que registra se a ação padrão de um evento foi cancelada com sucesso.defaultPrevented antes de decidir como tratar um clique.Propriedade key
key informa o valor produzido por uma tecla, levando em conta a disposição do teclado e modificadores como Shift.key de uma letra de "a" para "A" enquanto sua posição permanece a mesma.Propriedade pointerId
pointerId identifica um ponteiro individual durante uma interação para que contatos simultâneos possam ser tratados separadamente.Propriedade relatedTarget
relatedTarget identifica o outro elemento envolvido em uma transição de foco ou é null quando não há esse elemento disponível.focusout de um menu verifica se o destino permanece dentro do menu.Propriedade target
target identifica a origem do evento, que pode ser um elemento aninhado dentro do controle cuja ação está sendo tratada.target do clique.Propriedade value
value expõe o valor atual de um controle de formulário como texto, incluindo entradas numéricas cujo conteúdo ainda precisa de conversão."8" precisa de conversão antes que o código adicione um incremento numérico.Propriedade valueAsNumber
valueAsNumber lê diretamente o valor numérico de uma entrada compatível e retorna NaN quando não há um valor numérico utilizável.NaN, que o código detecta com Number.isNaN().R
Redefinição do formulário
form.reset() restaura os valores iniciais dos campos do formulário.Referência a uma função
openPanel registra a função; passar openPanel() a chama durante a configuração.Remoção de espaços em branco nas extremidades
name.value.trim() === "" detecta um nome composto inteiramente de espaços.Resumo de erros
T
tabindex itinerante
Teste de clique fora
V
Validação de restrições
Validade personalizada
setCustomValidity(), na qual uma mensagem não vazia marca o controle como inválido.Sobre esta aula
Crie seus próprios Materiais de Estudo
Transforme suas anotações, slides e documentos em Guias de Estudo, Questões de Prática, Flashcards e mais 6 ferramentas.