Skip to content
This page has been auto-translated and may contain errors.View in English

Eventos

docs.scrimba.com

Uma página que nunca reage é apenas um documento. No momento em que você quer que algo aconteça quando uma pessoa clica em um botão, digita em uma caixa de pesquisa ou envia um formulário, você precisa de uma forma de dizer "quando isso acontecer, execute este código". É isso que são eventos. Este capítulo é sobre escutar as coisas que um usuário faz e responder a elas.

Escutando um evento

Um evento é algo que acontece na página: um clique, uma tecla pressionada, um formulário sendo enviado. Para responder a um, você escolhe um elemento, nomeia o evento que lhe interessa e passa uma função para executar quando isso acontecer.

Essa função é chamada de handler (manipulador). O método que a vincula é addEventListener:

js
const button = document.querySelector(".buy-button");

button.addEventListener("click", () => {
  console.log("Alguém clicou em comprar");
});

Leia da esquerda para a direita: neste botão, escute por "click", e quando isso acontecer, execute esta função. O handler é executado toda vez que o evento acontece, não apenas uma vez. Clique três vezes e ele será executado três vezes.

Um evento é um sinal que o navegador dispara quando algo acontece: o usuário clica, digita, envia um formulário ou a página termina de carregar. Você responde registrando um handler com addEventListener, que recebe o nome do evento como uma string e uma função para chamar cada vez que ele dispara.

js
const button = document.querySelector(".buy-button");

const handleBuy = () => {
  console.log("Alguém clicou em comprar");
};

button.addEventListener("click", handleBuy);

Note que handleBuy é passado sem parênteses. Você está entregando a função em si ao navegador para chamar mais tarde, não a chamando agora. Escrever addEventListener("click", handleBuy()) a chamaria imediatamente e registraria tudo o que ela retornasse, que é um erro comum no início.

Um evento é uma mensagem que o navegador envia para qualquer código que registrou interesse nela. Você registra com addEventListener(type, handler): o tipo é o nome do evento como uma string, o handler é um callback que o navegador invoca cada vez que o evento dispara naquele elemento.

js
const button = document.querySelector(".buy-button");

const handleBuy = () => {
  console.log("Alguém clicou em comprar");
};

button.addEventListener("click", handleBuy);

Duas coisas importam aqui. Primeiro, você passa handleBuy, o valor da função, não handleBuy(), o resultado de chamá-la. Segundo, addEventListener é aditivo: chame-o duas vezes com duas funções e ambas serão executadas, então os listeners se acumulam em vez de sobrescrever. Essa é a razão prática pela qual ela existe em vez de atribuir button.onclick, que mantém um único handler e substitui qualquer um anterior. Manter uma referência nomeada ao handler também é importante mais tarde, quando você quer removê-lo.

Os handlers são funções ordinárias, e os elementos aos quais você os anexa vêm do DOM. Se você conseguir selecionar um elemento e escrever uma função, você pode responder ao que o usuário faz com ele.

JunoEscutando um evento Para responder a um usuário, escolha um elemento, nomeie o evento como uma string como "click", e dê a addEventListener uma função para executar. Essa função é o handler, e ela é executada toda e cada vez que o evento acontece, não apenas uma vez. Este único método está por trás de quase tudo o que uma página faz em resposta a você.
JunoEscutando um eventoaddEventListener recebe um nome de evento e uma função, e chama essa função cada vez que o evento dispara. Passe a função por nome sem parênteses: handleBuy, não handleBuy(). Adicionar os parênteses a chama imediatamente em vez de no clique, e isso confunde quase todo mundo uma vez.
JunoEscutando um eventoaddEventListener registra um callback que o navegador dispara em cada evento correspondente, e ele se acumula em vez de sobrescrever, e é por isso que você o prefere em relação a onclick. Passe o valor da função, não o resultado da sua chamada, então handleBuy e nunca handleBuy(). Mantenha uma referência nomeada por perto também, porque você vai querer em volta quando chegar a hora de remover o listener.

O objeto de evento

Quando o navegador executa seu handler, ele o entrega uma informação: um objeto de evento descrevendo o que aconteceu. Capture-o dando ao seu handler um parâmetro, geralmente nomeado event:

js
const button = document.querySelector(".buy-button");

button.addEventListener("click", (event) => {
  console.log(event.target); // o elemento que foi clicado
});

A parte mais útil é event.target: o elemento em que o evento aconteceu. Se o clique foi no botão, event.target é esse botão. Você não precisa selecioná-lo novamente, o evento já te diz de onde veio.

Todo handler recebe um argumento: o objeto de evento, que carrega detalhes sobre o que aconteceu. Você o captura nomeando um parâmetro, convencionalmente event (ou e):

js
const search = document.querySelector(".search-input");

search.addEventListener("input", (event) => {
  console.log(event.target.value); // o que o usuário digitou até agora
});

event.target é o elemento em que o evento disparou. Em campos de formulário, ele é o destaque principal, porque event.target.value fornece o texto atual do input. Esse emparelhamento, evento input mais event.target.value, é como você lê o que alguém está digitando enquanto digita.

O navegador passa ao seu handler um único argumento, o objeto de evento, um instantâneo do que aconteceu: qual elemento, qual tecla, a posição do mouse e métodos para controlar o comportamento padrão do evento. Você o captura com um parâmetro, convencionalmente event.

js
const search = document.querySelector(".search-input");

search.addEventListener("input", (event) => {
  console.log(event.target.value); // texto atual no campo
});

event.target é o elemento que originou o evento, que nem sempre é o elemento ao qual você anexou o listener (aquele é event.currentTarget, e a diferença entre eles é o que faz a delegação funcionar, abaixo). Em controles de formulário, event.target.value lê o valor atual como uma string, então o mesmo .value que o HTML mantém como texto volta como texto aqui. Procure um número e você o converte você mesmo com Number(event.target.value).

JunoO objeto de evento Seu handler recebe um argumento, o objeto de evento, então dê a ele um parâmetro nomeado event para capturá-lo. A parte que você vai usar mais é event.target, o elemento em que o evento aconteceu. Sem necessidade de selecioná-lo novamente, o evento já sabe de onde veio.
JunoO objeto de evento Nomeie um parâmetro event e o navegador o preencherá com detalhes sobre o que aconteceu. event.target é o elemento em que disparou, e em um input, event.target.value é o texto atualmente no campo. Emparelhe o evento input com event.target.value e você pode ler o que alguém digita enquanto digita.
JunoO objeto de evento O objeto de evento é um instantâneo do que aconteceu, e event.target é onde começou, que nem sempre é onde você anexou o listener; aquele é event.currentTarget. Em campos de formulário event.target.value volta como uma string, então envolva em Number(...) quando você precisar de um número. Mantenha a divisão alvo-versus-currentTarget em mente, porque a delegação depende disso.

Eventos comuns

Um punhado de eventos cobre a maioria do que você vai construir:

  • click: o usuário clica ou toca em algo.
  • input: o valor de um campo de texto muda enquanto eles digitam.
  • submit: um formulário é enviado.
  • keydown: uma tecla é pressionada.

O complicado é submit. Por padrão, enviar um formulário recarrega a página, o que anula tudo o que seu JavaScript estava fazendo. Para parar isso, chame event.preventDefault() dentro do handler:

js
const form = document.querySelector("form");

form.addEventListener("submit", (event) => {
  event.preventDefault(); // parar a página de recarregar
  console.log("Formulário enviado, página fica no lugar");
});

A maioria das páginas conta com um pequeno conjunto de eventos: click para botões, input para campos de texto enquanto mudam, submit para formulários e keydown para atalhos de teclado. Aprenda esses quatro e você pode construir a maioria das interações.

O que pega as pessoas é submit. O comportamento padrão de um formulário é recarregar a página ou navegar, o que descarta o que quer que seu código estivesse manipulando. Chame event.preventDefault() para manter a página no lugar e assuma o controle você mesmo:

js
const form = document.querySelector("form");

const handleSubmit = (event) => {
  event.preventDefault();
  const email = form.querySelector(".email-input").value;
  console.log(`Se inscrever ${email}`);
};

form.addEventListener("submit", handleSubmit);

Escute submit no próprio formulário, não click no botão, para que usuários de teclado que pressionam Enter também sejam cobertos.

Quatro eventos carregam a maioria das interações: click, input (dispara a cada mudança de valor, ao contrário de change que espera por blur), submit e keydown. Vincule-os ao elemento certo e a maioria da interface cai deles.

O evento submit é onde preventDefault ganha seu lugar. A ação padrão de um formulário é uma navegação completa, que descarta seu estado na página. Chamar event.preventDefault() cancela isso para que você manipule os dados você mesmo:

js
const form = document.querySelector("form");

const handleSubmit = (event) => {
  event.preventDefault();
  const email = form.querySelector(".email-input").value;
  console.log(`Se inscrever ${email}`);
  // enviá-lo para um servidor é o trabalho do próximo capítulo
};

form.addEventListener("submit", handleSubmit);

Anexe ao submit do formulário, não ao click do botão: submit dispara para a tecla Enter, clique no botão e envio programático, então é o gancho correto. Quando você tem o valor, enviá-lo para algum lugar é onde o código async entra, que é o próximo capítulo.

Bubbling e delegação

Quando você clica em um elemento, o evento não para lá. Ele bubbles (borbulha): dispara no elemento clicado, depois em seu pai, depois no pai daquele pai, tudo o caminho para cima. É por isso que um listener em um pai ouve cliques em seus filhos.

Esse comportamento alimenta delegação: anexe um listener a um pai e use event.target para descobrir qual filho foi realmente clicado. Em vez de um listener por item, você obtém um que cobre todos eles.

js
const list = document.querySelector(".todo-list");

list.addEventListener("click", (event) => {
  if (event.target.matches(".delete")) {
    event.target.closest("li").remove();
  }
});

O benefício: isso manipula itens adicionados à lista mais tarde. Um listener por item só vincula elementos que existem agora; um listener delegado no pai detecta cliques em filhos que não existiam quando o código foi executado, porque o evento ainda borbulha até o pai que está escutando.

Removendo listeners

removeEventListener desanexa um handler, mas apenas se você passar a mesma referência de função exata que adicionou:

js
const button = document.querySelector(".buy-button");

button.addEventListener("click", handleBuy);
button.removeEventListener("click", handleBuy); // funciona: mesma referência

É por isso que handlers anônimos não podem ser removidos. Uma função arrow inline como addEventListener("click", () => {...}) cria uma função nova cada vez, então você não tem referência para passar para removeEventListener, e o listener fica preso. Handlers nomeados que você planeja remover evitam isso. Listeners que sobrevivem aos elementos que tocam são uma verdadeira fonte de vazamentos de memória, onde objetos que a página não precisa mais não podem ser recuperados porque um listener ainda aponta para eles.

Uma nota de desempenho a mais: adicionar { passive: true } como um terceiro argumento em listeners de scroll ou toque promete que você não vai chamar preventDefault, que deixa o navegador rolar sem esperar seu handler e mantém o scroll suave.

JunoEventos comuns Os eventos que você vai usar mais são click, input, submit e keydown. O que você deve lembrar é submit: um formulário recarrega a página por padrão, então chame event.preventDefault() no handler para parar isso e manter o controle. Perca isso e sua página vai recarregar debaixo de você, que é confuso a primeira vez.
JunoEventos comunsclick, input, submit e keydown cobrem a maioria do que você constrói. Escute por submit no formulário, não click no botão, para que Enter funcione para usuários de teclado. E chame event.preventDefault() em um handler submit, ou a página recarrega e leva seu estado com ela.
JunoEventos comuns Eventos borbulham até os pais, então um listener delegado mais event.target cobre muitos filhos, incluindo os adicionados mais tarde. removeEventListener precisa da mesma referência de função que você adicionou, e é por isso que handlers anônimos nunca podem ser removidos e podem vazar memória. E submit vence um click de botão porque dispara para Enter também; mantenha event.preventDefault() lá para parar o recarregamento.

Por onde os eventos vão daqui

Eventos são como uma página escuta. Você escolhe um elemento, nomeia o que lhe interessa e executa uma função quando isso acontece, lendo os detalhes do objeto de evento enquanto avança. Quase todos os recursos interativos são construídos a partir desse loop.

O próximo passo natural é fazer algo lento em um handler, como enviar um formulário para um servidor ou carregar dados novos, sem congelar a página. É para isso que o código async serve, e é o próximo capítulo. Handlers que chegam em dados estruturados, como uma lista de itens que você mantém em objetos, também contam com o que você já sabe.