JavaScript

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

7 min de leitura

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

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

<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=&quot;...&quot;</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(&#039;meuBotao&#039;);

botao.addEventListener(&#039;click&#039;, function(event) {

console.log(&#039;Botão clicado!&#039;);

console.log(&#039;Alvo:&#039;, event.target);

});

// Exemplo 2: Múltiplos listeners no mesmo elemento

botao.addEventListener(&#039;mouseenter&#039;, () =&gt; {

botao.style.backgroundColor = &#039;#3498db&#039;;

});

botao.addEventListener(&#039;mouseleave&#039;, () =&gt; {

botao.style.backgroundColor = &#039;#2c3e50&#039;;

});

// Exemplo 3: Remover listener (necessário guardar referência)

function handleClick(event) {

console.log(&#039;Removerei este listener&#039;);

}

botao.addEventListener(&#039;click&#039;, handleClick);

botao.removeEventListener(&#039;click&#039;, 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(&#039;scroll&#039;, () =&gt; {

console.log(&#039;Scrollando...&#039;);

}, { passive: true });

// Executa apenas uma vez

document.addEventListener(&#039;DOMContentLoaded&#039;, () =&gt; {

console.log(&#039;Documento carregado!&#039;);

}, { 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">&lt;div id=&quot;avô&quot; style=&quot;padding: 20px; background: #ecf0f1;&quot;&gt;

&lt;div id=&quot;pai&quot; style=&quot;padding: 20px; background: #bdc3c7;&quot;&gt;

&lt;button id=&quot;filho&quot;&gt;Clique aqui&lt;/button&gt;

&lt;/div&gt;

&lt;/div&gt;

&lt;script&gt;

const avô = document.getElementById(&#039;avô&#039;);

const pai = document.getElementById(&#039;pai&#039;);

const filho = document.getElementById(&#039;filho&#039;);

// Fase de BUBBLING (padrão)

filho.addEventListener(&#039;click&#039;, () =&gt; console.log(&#039;1. Filho&#039;));

pai.addEventListener(&#039;click&#039;, () =&gt; console.log(&#039;2. Pai&#039;));

avô.addEventListener(&#039;click&#039;, () =&gt; console.log(&#039;3. Avô&#039;));

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

// Usando capture (true como terceiro parâmetro)

avô.addEventListener(&#039;click&#039;, () =&gt; console.log(&#039;A. Avô (captura)&#039;), true);

pai.addEventListener(&#039;click&#039;, () =&gt; console.log(&#039;B. Pai (captura)&#039;), true);

filho.addEventListener(&#039;click&#039;, () =&gt; console.log(&#039;C. Filho (captura)&#039;), true);

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

&lt;/script&gt;</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(&#039;form&#039;);

form.addEventListener(&#039;submit&#039;, (event) =&gt; {

event.preventDefault(); // Impede envio do formulário

console.log(&#039;Validação customizada...&#039;);

// Aqui você faria validações

if (validado) {

form.submit(); // Envia manualmente se válido

}

});

const link = document.querySelector(&#039;a&#039;);

link.addEventListener(&#039;click&#039;, (event) =&gt; {

event.stopPropagation(); // Para bubbling, mas permite comportamento padrão

event.preventDefault(); // Previne navegação

console.log(&#039;Link interceptado&#039;);

});</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">&lt;ul id=&quot;lista&quot;&gt;

&lt;li class=&quot;item&quot;&gt;Item 1&lt;/li&gt;

&lt;li class=&quot;item&quot;&gt;Item 2&lt;/li&gt;

&lt;li class=&quot;item&quot;&gt;Item 3&lt;/li&gt;

&lt;/ul&gt;

&lt;button id=&quot;adicionarItem&quot;&gt;Adicionar item&lt;/button&gt;

&lt;script&gt;

const lista = document.getElementById(&#039;lista&#039;);

const botao = document.getElementById(&#039;adicionarItem&#039;);

// SEM delegação (problema com elementos novos)

// document.querySelectorAll(&#039;.item&#039;).forEach(item =&gt; {

// item.addEventListener(&#039;click&#039;, handler); // Não funciona para novos itens!

// });

// COM delegação (abordagem correta)

lista.addEventListener(&#039;click&#039;, (event) =&gt; {

// Verifica se o clique foi em um .item

if (event.target.classList.contains(&#039;item&#039;)) {

console.log(&#039;Clicou em:&#039;, event.target.textContent);

event.target.style.backgroundColor = &#039;#f1c40f&#039;;

}

});

// Adicionar items dinamicamente

botao.addEventListener(&#039;click&#039;, () =&gt; {

const novoItem = document.createElement(&#039;li&#039;);

novoItem.className = &#039;item&#039;;

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(&#039;lista&#039;);

container.addEventListener(&#039;click&#039;, (event) =&gt; {

const item = event.target.closest(&#039;.item&#039;); // Sobe a árvore procurando

if (item) {

console.log(&#039;Item encontrado:&#039;, item.textContent);

}

});

&lt;/script&gt;</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>

Comentários

Mais em JavaScript

Event Loop em JavaScript: Call Stack, Task Queue e Microtasks na Prática
Event Loop em JavaScript: Call Stack, Task Queue e Microtasks na Prática

Call Stack: O Coração da Execução O Call Stack é uma estrutura de dados que r...

Generators e Iteradores em JavaScript na Prática
Generators e Iteradores em JavaScript na Prática

Iteradores: A Base de Tudo Um iterador é um objeto JavaScript que implementa...

Como Usar Iteração Avançada em JavaScript: Iterables, Generators Assíncronos em Produção
Como Usar Iteração Avançada em JavaScript: Iterables, Generators Assíncronos em Produção

Iterables e o Protocolo de Iteração Um iterable é um objeto que implementa o...