<h2>addEventListener: Fundamentos e Sintaxe</h2>
<p>O método <code>addEventListener</code> é a forma moderna e recomendada de vincular eventos a elementos do DOM. Diferentemente da abordagem inline (<code>onclick="..."</code>), ele permite múltiplos listeners no mesmo elemento, melhor separação de responsabilidades e acesso a recursos avançados como opções de captura.</p>
<p>A sintaxe básica é <code>elemento.addEventListener(evento, callback, opcoes)</code>. Aqui está um exemplo prático:</p>
<pre><code class="language-javascript">// 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);</code></pre>
<p>Uma vantagem crucial é o parâmetro <code>options</code>. Com <code>{ once: true }</code>, o listener executa apenas uma vez. Com <code>{ passive: true }</code>, melhora a performance em eventos como scroll:</p>
<pre><code class="language-javascript">window.addEventListener('scroll', () => {
console.log('Scrollando...');
}, { passive: true });
// Executa apenas uma vez
document.addEventListener('DOMContentLoaded', () => {
console.log('Documento carregado!');
}, { once: true });</code></pre>
<h2>Propagação: Entendendo Bubble e Capture</h2>
<p>Eventos no DOM não ocorrem apenas no elemento alvo—eles percorrem uma árvore em fases: <strong>captura</strong> (do topo para o alvo) e <strong>bubbling</strong> (do alvo de volta ao topo). Compreender isso é essencial para evitar comportamentos inesperados.</p>
<pre><code class="language-html"><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></code></pre>
<p>Para parar a propagação, use <code>event.stopPropagation()</code>. Para prevenir o comportamento padrão (como envio de formulário), use <code>event.preventDefault()</code>:</p>
<pre><code class="language-javascript">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');
});</code></pre>
<h2>Delegação de Eventos: Eficiência em Escala</h2>
<p>Delegação é uma técnica poderosa: ao invés de adicionar listeners em cada elemento, você adiciona um no container pai e usa <code>event.target</code> para identificar qual elemento foi clicado. Isso economiza memória e funciona automaticamente com elementos criados dinamicamente.</p>
<pre><code class="language-html"><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></code></pre>
<p>A delegação é especialmente útil em listas, tabelas e componentes dinâmicos. Use <code>event.target.closest(selector)</code> para encontrar o elemento mais próximo que corresponde ao seletor.</p>
<h2>Conclusão</h2>
<p>Dominar eventos no DOM significa: (1) usar <code>addEventListener</code> para flexibilidade e múltiplos handlers, (2) compreender a propagação (bubbling e captura) e controlar com <code>stopPropagation()</code> 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.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener" target="_blank" rel="noopener noreferrer">MDN: addEventListener</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#event_bubbling_and_capture" target="_blank" rel="noopener noreferrer">MDN: Event bubbling and capturing</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#event_delegation" target="_blank" rel="noopener noreferrer">MDN: Event delegation</a></li>
<li><a href="https://javascript.info/bubbling-and-capturing" target="_blank" rel="noopener noreferrer">JavaScript.info: Bubbling and capturing</a></li>
<li><a href="https://web.dev/bfcache/#event-listeners" target="_blank" rel="noopener noreferrer">Web.dev: Event listeners best practices</a></li>
</ul>