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).
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.
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.
É 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. ;)