event.target vs event.currentTarget: qual usar?

É muito comum você conhecer o event.target mas ainda não ter ouvido falar no event.currentTarget. Entenda a diferença.

  • evento javascript
  • target
  • currentTarget
  • addEventListener
  • javascript
Front-end

Shortcut

Em alguns momentos, você precisará trocar o event.target pelo event.currentTarget.

Se você conhece o event.target, é tentador usar ele em situações como essa.

Imagine que você tem um ecommerce, e essa é sua página de produto:

página de produto de um ecommerce, à esquerda mostra a foto de destaque do produto que é uma roupa, e à direita 4 miniaturas com cores diferentes do produto
Fonte: https://www.uplabs.com/posts/shopify-ecommerce-website-template-02

Nela, o usuário vê a imagem de destaque à esquerda, e as minuatura do produto à direita. E digamos que você queira mudar a imagem de destaque, a cada clique nas miniaturas. Para isso, é preciso descobrir em qual cor o usuário clicou.

Infelizmente, usar o event.target aqui pode causar alguns probleminhas.

Veja o que pode acontecer.

O problema do event.target

Abaixo está a seção com as miniaturas das cores:

<button class="color-button" data-color="blue">
  <img src="blue.png" />
</button>
<button class="color-button" data-color="gray">
  <img src="gray.png" />
</button>
<button class="color-button" data-color="red">
  <img src="red.png" />
</button>
<button class="color-button" data-color="green">
  <img src="green.png" />
</button>

Perceba que cada botão possui um atributo data-color com a sua cor. Isso será importante daqui a pouco.

Agora veja como fica o script para adicionar um evento de clique nesses botões:

// Pegar os botões
const colorButtons = document.querySelectorAll('.color-button');

// Iterar por eles, e adicionar um evento
// que muda a foto principal com base na cor
colorButtons.forEach(function(button) {
  button.addEventListener('click', changePictureByColor);

// Criar a função do evento
function changePictureByColor(event) {
  // ...
}

Até aqui tudo bem.

Agora, na função changePictureByColor você precisa descobrir qual foi a cor clicada.

Já que a cor está no atributo data-color de cada botão, basta pegar o seu valor:

function changePictureByColor(event) {
  const clickedColor = event.target.getAttribute('data-color');
  // ...
}

Agora com a variável clickedColor em mãos, você pode seguir com a função e trocar a imagem em destaque.

Certo? Errado!

A sua função começa a estourar erros e mais erros... E após muito suor, você descobre que a variável clickedColor retorna null. Mas como?!

Como obter o elemento HTML alvo do evento

Console, eu escolho você!

Você então invoca uma chuva de console.log até descobrir que event.target não aponta para o botão. E sim para a imagem dentro do botão. E como a imagem não tem um atributo data-color, o caos começa.

Veja algumas formas de corrigir isso.

Adicionar o atributo data-color na imagem

Essa é a solução que gosto menos.

<button class="color-button">
  <img src="blue.png" data-color="blue" />
</button>
<button class="color-button">
  <img src="gray.png" data-color="gray" />
</button>
<button class="color-button">
  <img src="red.png" data-color="red" />
</button>
<button class="color-button">
  <img src="green.png" data-color="green" />
</button>

Isso porque, caso o usuário clique na pontinha do botão, ali onde a tag <img> já não existe mais, o event.target será o botão. E nesse caso, ele não possui o data-color.

Outra opção é...

Adicionar o atributo data-color na imagem e no botão

Antes eu estava de brincadeira. ESSA SIM é a solução que gosto menos.

Alguns dos motivos são:

  • O dado fica repetido em mais de um lugar

  • Caso existam outros elementos dentro de <button>, todos eles precisariam do atributo.

Uma solução um pouco melhor (que usei por muito tempo) é...

Usar o método closest() do Javascript

Voltarei com o HTML original, com o data-color no botão:

<button class="color-button" data-color="blue">
  <img src="blue.png" />
</button>
<button class="color-button" data-color="gray">
  <img src="gray.png" />
</button>
<button class="color-button" data-color="red">
  <img src="red.png" />
</button>
<button class="color-button" data-color="green">
  <img src="green.png" />
</button>

Agora vou adicionar algumas linhas na função, para garantir que peguei o botão:

function changePictureByColor(event) {
  const clickedButton = event.target.closest('.color-button'); // pegar botão
  const clickedColor = clickedButton.getAttribute('data-color'); // pegar cor
  // ...
}

O método closest() recebe um seletor e retorna o primeiro elemento que corresponda a esse seletor. Porém em vez de buscar na página inteira, o método sobe na árvore HTML.

Então quando executo event.target.closest('.color-button'), os passos que a função closest() executa são os seguintes:

  1. event.target (a imagem) possui a classe color-button? Não, então executa novamente com o elemento pai da imagem

  2. <button class="color-button"> possui a classe color-button? Sim, então retorna ele

  3. Fim

Caso chegue até o elemento raiz <html> e não encontre, retorna null.

Assim, tenho a certeza de que agora peguei o botão. Agora posso pegar a cor clicada e prosseguir.

Usei essa solução por muito tempo me achando o máximo. Até descobrir que posso...

Usar event.currentTarget em vez de event.target

E só isso!

function changePictureByColor(event) {
  const clickedColor = event.currentTarget.getAttribute('data-color');
  // ...
}

Assim, a variável clickedColor sempre vai receber a cor presente no atributo data-color do botão.

Mas que magia aconteceu aqui?

O event.target aponta para o elemento que dispara o evento, ou seja, aquele que cliquei de fato. Isso pode ser o próprio botão, ou qualquer elemento dentro dele, como a imagem. Assim, em cada clique, event.target pode ser um elemento HTML diferente.

Já o event.currentTarget aponta sempre para o mesmo elemento. Aquele que foi atribuído ao addEventListener.

No exemplo do ecommerce que você acabou de ver, event.target e event.currentTarget apontam para elementos diferentes.

Callback

Como dito, a propriedade event.target aponta para o elemento que disparou o evento.

Já o event.currentTarget aponta para o elemento que possui o event listener associado a ele.

Fique atento para usar cada propriedade no momento certo.

Veja outros posts sobre Front-end