Manipular eventos JavaScript faz parte do dia-a-dia de qualquer programador front end e é tarefa banal.
Mas você saber como eventos em JavaScript realmente funcionam? 🤔
Um exemplo de Eventos JavaScript
Talvez tudo o que você (pensava que) sabia sobre como eventos JavaScript funcionam esteja errado. Então, prepara-se para ter seu mundo abalado!
Para a demonstração, suponha uma hierarquia simples de elementos: 3 div
aninhadas, cada uma com sua respectiva classe, “one”, “two” e “three”.
1 2 3 4 5 6 |
<div class="one"> <div class="two"> <div class="three"> </div> </div> </div> |
A aparência disso não é tão importante, mas, se quiser acompanhar mais visualmente, assista ao vídeo no início do artigo.
Usando um loop simples de JavaScript, um evento de click será associado a cada uma dessas div
.
1 2 3 4 5 6 7 8 9 10 |
const $divs = document.querySelectorAll('div') $divs.forEach( $div => $div.addEventListener('click', logClass) ) function logClass() { console.log(this.classList.value) } |
Todas as div
agora possuem um evento de click
associado (para mostrar o nome de sua classe no console). Isso é muito importante para entender o restante.
Ao seu clicar na div mais interna, .three
, o que você acha que vai ser logado no console?
- one
- two
- three
Acertou quem pensou:
1 2 3 |
three two one |
Exatamente, as 3 classes serão logadas!
Se você não entendeu o motivo disso, nesse ponto, é preciso explicar como as coisas realmente funcionam quando um evento JavaScript é disparado.
Eventos JavaScript: como as coisas realmente funcionam
Quando um evento JavaScript é acionado, existem 2 fases associadas:
- Capturing (Captura)
- Bubbling (“Borbulhamento” ou Propagação)
Fase de Capturing (Fase 1)
Por padrão, o evento de click
(assim como quase todos os outros eventos em JavaScript) não acontece exatamente a partir do elemento que foi clicado.
Ao invés disso, o evento começa no nó root (window
) e vai descendo por toda a hierarquia DOM até chegar no elemento em questão (o target).
Essa é a chamada Fase de Capturing ou Captura (ou Fase 1).
![Eventos JavaScript: esquematização de como o evento click começa no elemento root, window.](https://storage.googleapis.com/dpw/app/uploads/2020/08/2-1024x576.png)
Nisso que ele vai passando por cada um dos elementos da hierarquia, caso haja algum evento do mesmo tipo respectivamente associado, esse evento será disparado.
![Eventos JavaScript: esquematização da Fase de Capturing.](https://storage.googleapis.com/dpw/app/uploads/2020/08/3-1024x576.png)
Fase de Bubbling (Fase 2)
Depois de percorrer todo esse caminho e finalmente encontrar o target, você acha que ele para? Não, não, não… Ele faz o caminho inverso pela mesma hierarquia até retornar ao elemento root (window
).
Esse retorno é a chamada “Fase 2”, mais conhecida como Fase de Bubbling ou “Borbulhamento”.
Mas um outro nome é Fase de Propagação, em um certo sentido até mais condizente ao que acontece. O que é “Propagar”? É difundir; divulgar; espalhar… E é justamente isso que acontece.
Nesse caminho de volta ao root, novamente, se houver algum evento do mesmo tipo respectivamente associado a algum elemento da hierarquia, esse evento será disparado.
![Eventos JavaScript: esquematização da Fase de Bubbling.](https://storage.googleapis.com/dpw/app/uploads/2020/08/4-1024x576.png)
É por isso que, no exemplo, foi logado “three, two, one”, nessa ordem: dentro dessa hierarquia do aninhamento das div
, na Fase de Bubbling primeiro ele disparou o evento da div
mais interna, depois da central e, por fim, da mais externa.
Opções para Event Listeners
Na sintaxe mais atual de JavaScript, é admitido um objeto como 3º parâmetro de addEventListener
para informar opções relacionadas a esse listener.
Uma dessas opções é capture: true
, que alteraria o código-exemplo para:
1 2 3 4 5 |
$divs.forEach( $div => $div.addEventListener('click', logClass, { capture: true }) ) |
Essa opção significa que se está querendo trabalhar com o evento na Fase de Captura (como sugere o próprio nome da opção). Então, agora, ao se clicar na div mais interna, será logado:
1 2 3 |
one two three |
Já que, na hierarquia, é disparado o evento click
do elemento mais externo para o mais interno! 🤯
Como interromper a propagação de um evento JavaScript
O JavaScript também oferece uma maneira de ter um controle mais acurado dessa dinâmica de eventos, permitindo interromper a propagação de eventos.
Alterando a opção capture
para seu default false
, faça-se um acréscimo à função que faz o log da classe:
1 2 3 4 5 |
function logClass(e) { e.stopPropagation() console.log(this.classList.value) } |
Com isso, ao se clicar em qualquer um dos elementos, ele vai logar unicamente a respectiva classe do que foi clicado, já que a propagação foi interrompida.
Então, diz aí: o que vai acontecer ao se usar o stopPropagation()
juntamente com capture: true
?
Para saber a resposta, assista o vídeo no início do artigo até o final. ;)