Eventos no DOM: addEventListener, Delegação e Propagação: Do Básico ao Avançado Já leu

addEventListener: Fundamentos e Sintaxe O método é a forma moderna e recomendada de vincular eventos a elementos do DOM. Diferentemente da abordagem inline ( ), ele permite múltiplos listeners no mesmo elemento, melhor separação de responsabilidades e acesso a recursos avançados como opções de captura. A sintaxe básica é . Aqui está um exemplo prático: Uma vantagem crucial é o parâmetro . Com , o listener executa apenas uma vez. Com , melhora a performance em eventos como scroll: Propagação: Entendendo Bubble e Capture Eventos no DOM não ocorrem apenas no elemento alvo—eles percorrem uma árvore em fases: captura (do topo para o alvo) e bubbling (do alvo de volta ao topo). Compreender isso é essencial para evitar comportamentos inesperados. Para parar a propagação, use . Para prevenir o comportamento padrão (como envio de formulário), use : Delegação de Eventos: Eficiência em Escala Delegação é uma técnica poderosa: ao invés de adicionar listeners em cada elemento, você adiciona um no

addEventListener: Fundamentos e Sintaxe

O método addEventListener é a forma moderna e recomendada de vincular eventos a elementos do DOM. Diferentemente da abordagem inline (onclick="..."), ele permite múltiplos listeners no mesmo elemento, melhor separação de responsabilidades e acesso a recursos avançados como opções de captura.

A sintaxe básica é elemento.addEventListener(evento, callback, opcoes). Aqui está um exemplo prático:

// Exemplo 1: Clique simples
const botao = document.getElementById('meuBotao');

botao.addEventListener('click', function(event) {
  console.log('Botão clicado!');
  console.log('Alvo:', event.target);
});

// Exemplo 2: Múltiplos listeners no mesmo elemento
botao.addEventListener('mouseenter', () => {
  botao.style.backgroundColor = '#3498db';
});

botao.addEventListener('mouseleave', () => {
  botao.style.backgroundColor = '#2c3e50';
});

// Exemplo 3: Remover listener (necessário guardar referência)
function handleClick(event) {
  console.log('Removerei este listener');
}

botao.addEventListener('click', handleClick);
botao.removeEventListener('click', handleClick);

Uma vantagem crucial é o parâmetro options. Com { once: true }, o listener executa apenas uma vez. Com { passive: true }, melhora a performance em eventos como scroll:

window.addEventListener('scroll', () => {
  console.log('Scrollando...');
}, { passive: true });

// Executa apenas uma vez
document.addEventListener('DOMContentLoaded', () => {
  console.log('Documento carregado!');
}, { once: true });

Propagação: Entendendo Bubble e Capture

Eventos no DOM não ocorrem apenas no elemento alvo—eles percorrem uma árvore em fases: captura (do topo para o alvo) e bubbling (do alvo de volta ao topo). Compreender isso é essencial para evitar comportamentos inesperados.

<div id="avô" style="padding: 20px; background: #ecf0f1;">
  <div id="pai" style="padding: 20px; background: #bdc3c7;">
    <button id="filho">Clique aqui</button>
  </div>
</div>

<script>
const avô = document.getElementById('avô');
const pai = document.getElementById('pai');
const filho = document.getElementById('filho');

// Fase de BUBBLING (padrão)
filho.addEventListener('click', () => console.log('1. Filho'));
pai.addEventListener('click', () => console.log('2. Pai'));
avô.addEventListener('click', () => console.log('3. Avô'));

// Resultado: 1. Filho → 2. Pai → 3. Avô

// Usando capture (true como terceiro parâmetro)
avô.addEventListener('click', () => console.log('A. Avô (captura)'), true);
pai.addEventListener('click', () => console.log('B. Pai (captura)'), true);
filho.addEventListener('click', () => console.log('C. Filho (captura)'), true);

// Resultado: A. Avô → B. Pai → C. Filho → [depois bubbling]
</script>

Para parar a propagação, use event.stopPropagation(). Para prevenir o comportamento padrão (como envio de formulário), use event.preventDefault():

const form = document.querySelector('form');

form.addEventListener('submit', (event) => {
  event.preventDefault(); // Impede envio do formulário
  console.log('Validação customizada...');

  // Aqui você faria validações
  if (validado) {
    form.submit(); // Envia manualmente se válido
  }
});

const link = document.querySelector('a');
link.addEventListener('click', (event) => {
  event.stopPropagation(); // Para bubbling, mas permite comportamento padrão
  event.preventDefault(); // Previne navegação
  console.log('Link interceptado');
});

Delegação de Eventos: Eficiência em Escala

Delegação é uma técnica poderosa: ao invés de adicionar listeners em cada elemento, você adiciona um no container pai e usa event.target para identificar qual elemento foi clicado. Isso economiza memória e funciona automaticamente com elementos criados dinamicamente.

<ul id="lista">
  <li class="item">Item 1</li>
  <li class="item">Item 2</li>
  <li class="item">Item 3</li>
</ul>

<button id="adicionarItem">Adicionar item</button>

<script>
const lista = document.getElementById('lista');
const botao = document.getElementById('adicionarItem');

// SEM delegação (problema com elementos novos)
// document.querySelectorAll('.item').forEach(item => {
//   item.addEventListener('click', handler); // Não funciona para novos itens!
// });

// COM delegação (abordagem correta)
lista.addEventListener('click', (event) => {
  // Verifica se o clique foi em um .item
  if (event.target.classList.contains('item')) {
    console.log('Clicou em:', event.target.textContent);
    event.target.style.backgroundColor = '#f1c40f';
  }
});

// Adicionar items dinamicamente
botao.addEventListener('click', () => {
  const novoItem = document.createElement('li');
  novoItem.className = 'item';
  novoItem.textContent = `Item ${lista.children.length + 1}`;
  lista.appendChild(novoItem); // Listener já funciona aqui!
});

// Exemplo mais complexo: delegação com closest()
const container = document.getElementById('lista');

container.addEventListener('click', (event) => {
  const item = event.target.closest('.item'); // Sobe a árvore procurando
  if (item) {
    console.log('Item encontrado:', item.textContent);
  }
});
</script>

A delegação é especialmente útil em listas, tabelas e componentes dinâmicos. Use event.target.closest(selector) para encontrar o elemento mais próximo que corresponde ao seletor.

Conclusão

Dominar eventos no DOM significa: (1) usar addEventListener para flexibilidade e múltiplos handlers, (2) compreender a propagação (bubbling e captura) e controlar com stopPropagation() quando necessário, e (3) aplicar delegação de eventos para código mais limpo e eficiente, especialmente com conteúdo dinâmico. Esses três pilares são fundamentais para qualquer desenvolvedor JavaScript moderno.

Referências


Artigos relacionados