Delegação de eventos
1. Fundamentos da Delegação de Eventos
A delegação de eventos é uma técnica fundamental no desenvolvimento JavaScript que permite gerenciar eventos de forma eficiente, aproveitando o mecanismo de propagação (event bubbling) do DOM. Em vez de atribuir listeners individuais a cada elemento, você atribui um único listener a um elemento ancestral e utiliza a lógica condicional para tratar eventos originados em seus descendentes.
O mecanismo de propagação funciona da seguinte forma: quando um evento é disparado em um elemento, ele primeiro executa os handlers no próprio elemento (fase de captura), depois sobe pela árvore DOM até o documento raiz (fase de bubbling). A delegação explora principalmente a fase de bubbling.
// Estrutura básica de propagação
document.querySelector('#container').addEventListener('click', (event) => {
// event.target: elemento que originou o evento
// event.currentTarget: elemento onde o listener foi registrado (#container)
console.log('Alvo:', event.target.tagName);
console.log('Ouvinte:', event.currentTarget.id);
});
A diferença crucial entre event.target e event.currentTarget é que o primeiro sempre se refere ao elemento que disparou o evento, enquanto o segundo permanece constante como o elemento onde o listener foi registrado.
2. Implementando Delegação no DOM com JavaScript Puro
A implementação prática da delegação segue um padrão claro: selecionar um elemento pai, adicionar um único listener e identificar o elemento alvo através de event.target ou propriedades como closest().
// Exemplo: gerenciar cliques em uma lista dinâmica
const lista = document.querySelector('#minhaLista');
lista.addEventListener('click', (event) => {
const item = event.target.closest('li');
if (!item) return; // Ignora cliques fora dos itens
const acao = item.dataset.acao;
switch(acao) {
case 'editar':
editarItem(item);
break;
case 'excluir':
excluirItem(item);
break;
case 'selecionar':
selecionarItem(item);
break;
}
});
// Adicionando novos itens dinamicamente - sem necessidade de novos listeners
function adicionarItem(texto, acao) {
const li = document.createElement('li');
li.textContent = texto;
li.dataset.acao = acao;
lista.appendChild(li);
}
3. Vantagens da Delegação em Cenários Reais
A delegação oferece benefícios significativos em aplicações modernas:
Redução de memória: Em uma tabela com 1000 linhas, um único listener substitui 1000 listeners individuais.
Suporte a elementos dinâmicos: Elementos adicionados após o carregamento da página são automaticamente gerenciados.
// Exemplo: tabela grande com delegação
const tabela = document.querySelector('#tabelaClientes');
tabela.addEventListener('click', (event) => {
const linha = event.target.closest('tr');
if (!linha || linha === tabela) return;
const clienteId = linha.dataset.clienteId;
carregarDetalhesCliente(clienteId);
});
// Adicionar 1000 linhas sem preocupação com listeners
for (let i = 0; i < 1000; i++) {
adicionarLinhaTabela(`Cliente ${i}`, i);
}
4. Limitações e Cuidados com a Delegação
Nem todos os eventos propagam adequadamente para delegação. Eventos como focus, blur, scroll, load e error não fazem bubbling naturalmente.
// Eventos que NÃO propagam - requerem abordagens alternativas
elemento.addEventListener('focus', handler); // Não delega
elemento.addEventListener('scroll', handler); // Não delega
// Solução para foco: usar focusin/focusout (propagam)
pai.addEventListener('focusin', (event) => {
// event.target recebeu foco
});
O uso de stopPropagation() pode quebrar a cadeia de delegação. Evite-o em handlers delegados ou use com extrema cautela.
Em árvores DOM muito profundas, a delegação pode ter impacto de performance devido à verificação de cada ancestral até encontrar o elemento alvo.
5. Delegação de Eventos no Node.js (Contexto Server-Side)
No ambiente Node.js, o padrão de delegação de eventos é implementado através do módulo EventEmitter, que permite criar sistemas de eventos personalizados.
const EventEmitter = require('events');
class ProcessadorDeArquivos extends EventEmitter {
constructor() {
super();
this.delegarEventos();
}
delegarEventos() {
// Delega eventos específicos para handlers especializados
this.on('arquivo', (dados) => {
if (dados.tipo === 'imagem') {
this.emit('imagem:processar', dados);
} else if (dados.tipo === 'texto') {
this.emit('texto:processar', dados);
}
});
}
processarStream(stream) {
stream.on('data', (chunk) => {
// Delega para handlers baseados no tipo de dado
this.emit('dado', chunk);
});
}
}
const processador = new ProcessadorDeArquivos();
processador.on('imagem:processar', (dados) => {
console.log('Processando imagem:', dados.nome);
});
processador.on('texto:processar', (dados) => {
console.log('Processando texto:', dados.nome);
});
Diferente do cliente, no servidor os eventos são puramente programáticos e não envolvem interação direta com o DOM.
6. Delegação de Eventos no React
O React gerencia eventos através de synthetic events, que são wrappers dos eventos nativos. Por padrão, o React já utiliza delegação ao anexar listeners ao nó raiz do documento.
import React, { useState } from 'react';
function ListaTarefas() {
const [tarefas, setTarefas] = useState([
{ id: 1, texto: 'Estudar React', status: 'pendente' },
{ id: 2, texto: 'Praticar delegação', status: 'concluida' }
]);
const handleClick = (event) => {
const tarefaId = event.currentTarget.dataset.id;
const acao = event.target.dataset.acao;
if (acao === 'concluir') {
setTarefas(prev => prev.map(t =>
t.id === Number(tarefaId) ? { ...t, status: 'concluida' } : t
));
} else if (acao === 'excluir') {
setTarefas(prev => prev.filter(t => t.id !== Number(tarefaId)));
}
};
return (
<ul onClick={handleClick}>
{tarefas.map(tarefa => (
<li key={tarefa.id} data-id={tarefa.id}>
<span>{tarefa.texto}</span>
<button data-acao="concluir">✓</button>
<button data-acao="excluir">✗</button>
</li>
))}
</ul>
);
}
A abordagem nativa do React com synthetic events já oferece delegação automática, mas para casos específicos onde você precisa de mais controle, pode utilizar event.target e atributos data-*.
7. Padrões Avançados e Boas Práticas
Combinando delegação com throttling:
function throttle(fn, limit) {
let inThrottle = false;
return function(...args) {
if (!inThrottle) {
fn.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
container.addEventListener('scroll', throttle((event) => {
// Processa scroll delegado com controle de frequência
const elemento = event.target.closest('.item');
if (elemento) {
carregarMaisConteudo(elemento);
}
}, 200));
Debugging de eventos delegados:
// Helper para depuração
function debugDelegacao(event) {
console.group('Evento delegado');
console.log('Tipo:', event.type);
console.log('Target:', event.target);
console.log('CurrentTarget:', event.currentTarget);
console.log('Caminho:', event.composedPath());
console.groupEnd();
}
container.addEventListener('click', (event) => {
debugDelegacao(event);
// Lógica principal aqui
});
Boas práticas para delegação:
- Use closest() em vez de matches() para navegar para cima na árvore
- Evite delegação muito genérica - seja específico com seletores
- Documente claramente a hierarquia de eventos no código
- Considere o impacto em performance para árvores DOM muito profundas
Referências
- MDN Web Docs - Event bubbling and delegation — Documentação oficial da Mozilla explicando o mecanismo de bubbling e delegação de eventos no DOM
- JavaScript.info - Event Delegation — Tutorial completo e detalhado sobre delegação de eventos com exemplos práticos
- React Documentation - Handling Events — Documentação oficial do React sobre como o framework gerencia eventos e synthetic events
- Node.js Documentation - Events — Documentação oficial do módulo EventEmitter do Node.js para implementação de eventos no servidor
- CSS-Tricks - Event Delegation in JavaScript — Artigo técnico abrangente sobre delegação de eventos com casos de uso reais e boas práticas
- Eloquent JavaScript - Handling Events — Capítulo do livro Eloquent JavaScript sobre eventos, incluindo delegação e propagação
- David Walsh Blog - JavaScript Event Delegation — Tutorial prático e direto sobre implementação de delegação de eventos