Fluxo de Eventos no DOM
Quando um evento ocorre, ele se propaga pelos elementos da árvore DOM em uma ordem específica. Esse processo é chamado de fluxo de eventos DOM e é dividido em três fases:
- Fase de captura
- Fase de alvo
- Fase de borbulhamento
Em um documento HTML aninhado, um evento gerado em um elemento filho viaja do nó raiz (document) até o elemento alvo e depois retorna. Durante a captura, o evento desce do elemento mais externo (document) até o elemento alvo. No alvo, a função de tratamento é executada. No borbulhamento, o evento sobe do elemento alvo de volta ao documento.
Observações importantes:
- Código JavaScript só pode executar uma das fases (captura ou borbulhamento) por evento.
- Prorpiedades como
onclickeattachEventsó operam na fase de borbulhamento. - O método
addEventListener(type, listener, useCapture): seuseCapturefortrue, o listener é chamado na fase de captura; caso contrário (padrãofalse), na fase de borbulhamento. - Na prática, o borbulhamento é mais utilizado (captura não é suportada por IE antigo).
- Eventos como
focus,blur,mouseenteremouseleavenão borbulham.
O Objeto Evento
O objeto event (ou e) é passado automaticamente para a função de tratamento. Ele contém informações sobre o evento ocorrido. Para compatibilidade com navegadores antigos (IE 6-8), usa-se window.event.
event = event || window.event;
Propriedades e Métodos Comuns
| Propriedade/Método | Descrição |
|---|---|
e.target |
Elemento que disparou o evento (padrão W3C). |
e.srcElement |
Equivalente não padrão usado no IE antigo. |
e.type |
Tipo do evento (ex.: "click", "mouseover"). |
e.cancelBubble |
Propriedade não padrão para interromper o borbulhamento (IE). |
e.returnValue |
Propriedade não padrão para prevenir ação padrão (IE). |
e.preventDefault() |
Método padrão para prevenir ação padrão. |
e.stopPropagation() |
Método padrão para interromper a propagação (borbulhamento/captura). |
Diferença entre this e e.target
thisrefere-se ao elemento ao qual o listener foi vinculado.e.targetrefere-se ao elemento que realmente originou o evento.
const lista = document.querySelector('ul');
lista.addEventListener('click', function(e) {
console.log(this); // <ul>...
console.log(e.target); // <li> clicado
});
Prevenindo Ação Padrão
Três formas de impedir o comportamento padrão (ex.: seguir um link):
e.preventDefault();– padrão moderno.e.returnValue = false;– para IE antigo.return false;– funciona em todos, mas encerra a função imediatamnete.
Interrompendo a Propagação
const pai = document.querySelector('.pai');
const filho = document.querySelector('.filho');
pai.addEventListener('click', mostrarNome, false);
filho.addEventListener('click', mostrarNome, false);
document.addEventListener('click', function() {
alert('Document');
}, false);
function mostrarNome(event) {
alert(this.className);
event.stopPropagation(); // interrompe o borbulhamento
// event.cancelBubble = true; // para IE 6-8
}
Delegação de Eventos
A delegação de eventos consiste em anexar um único listener a um elemanto pai, em vez de adicionar listeners a cada filho. O evento de um filho borbulha até o pai, onde o listener captura o e.target para identificar o elemento clicado.
Por que usar?
- Reduz o número de listeners, melhorando a performance, especialmente com muitos elementos.
- Menos manipulação do DOM, evitando reflow e repaint desnecessários.
- Funciona para elementos adicionados dinamicamente após a vinculação do evento.
Exemplo Prático
Considere uma lista não ordenada:
<ul id="minhaLista">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
Abordagem comum (ineficiente): adicionar um evento mouseover a cada li.
document.querySelectorAll('#minhaLista li').forEach(function(item) {
item.addEventListener('mouseover', function() {
this.style.backgroundColor = 'gray';
// ... resetar irmãos
});
});
Com delegação: ouvir o evento no ul e identificar o item.
document.getElementById('minhaLista').addEventListener('mouseover', function(e) {
if (e.target.tagName === 'LI') {
// Remove destaque de todos os itens
this.querySelectorAll('li').forEach(function(li) {
li.style.backgroundColor = '';
});
e.target.style.backgroundColor = 'gray';
}
});
Assim, mesmo que novos itens sejam adicionados dinamicamente, eles herdam o comportamento.