W3docs

Bubbling e Capturing de Eventos JavaScript

Bubbling e capturing são duas fases do modelo de propagação de eventos que ocorre quando eventos são disparados no DOM (Document Object Model).

Dominando Event Bubbling e Capturing em JavaScript

Quando você clica em um botão, o clique não dispara apenas naquele botão — ele percorre todos os elementos ancestrais no caminho até o alvo e de volta. Esse percurso é chamado de propagação de eventos, e acontece em duas direções: capturing (descendo em direção ao alvo) e bubbling (subindo de volta à raiz). Entender ambos é essencial para lidar com eventos de forma confiável em aplicações reais, especialmente quando elementos aninhados têm seus próprios handlers.

Este guia explica o modelo de propagação, mostra como ouvir em cada fase e percorre as ferramentas práticas — event.target, event.currentTarget, stopPropagation() e delegação de eventos — que tornam esses conceitos úteis no dia a dia. Ele se baseia nos fundamentos abordados em Introdução a Eventos do Navegador e Eventos JavaScript.

Entendendo a Propagação de Eventos

A propagação de eventos no DOM ocorre em três fases, nesta ordem exata:

  1. Fase de capturing — o evento começa no topo da árvore (windowdocument<html> → …) e desce até o elemento alvo.
  2. Fase de alvo — o evento chega ao elemento com o qual você realmente interagiu.
  3. Fase de bubbling — o evento sobe de volta do alvo até a raiz.
                 │ capturing (down)    ▲ bubbling (up)
   <html>        ▼                     │
     <div>       ▼                     │
       <p> ───►  target (you clicked here)

Por padrão, handlers adicionados com addEventListener e atributos inline on* executam na fase de bubbling. Você opta pela fase de capturing explicitamente.

Event Bubbling

Na fase de bubbling, um evento começa no elemento mais específico (o nó mais profundo com o qual você interagiu) e então flui para cima por cada ancestral em direção ao document. Esse é o comportamento padrão para quase todos os eventos.

<div onclick="alert('You clicked the DIV!');">
  Click me or one of my children:
  <p onclick="alert('You clicked the P!');">Click me!</p>
</div>

Se você clicar no <p>, verá o alerta do <p> primeiro, depois o alerta do <div> à medida que o evento sobe. Se você clicar diretamente no <div> (fora do <p>), apenas o alerta do <div> é disparado — o evento nunca chega ao <p> porque <p> não é um ancestral do ponto de clique.

Event Capturing

O capturing é a primeira fase, onde o evento desce até o alvo. É usado com muito menos frequência do que o bubbling, mas é útil quando você precisa interceptar um evento antes que qualquer handler interno possa executar.

Para ouvir durante a fase de capturing, defina o terceiro argumento de addEventListener como true (ou passe { capture: true }):

<div id="outer">
  Click me or one of my children:
  <p id="inner">Click me!</p>
</div>

<script>
  document.getElementById('outer').addEventListener('click', function () {
    alert('Captured on DIV!');
  }, true); // true → capturing phase

  document.getElementById('inner').addEventListener('click', function () {
    alert('Captured on P!');
  }, true);
</script>

Clique no <p> e os alertas disparam de cima para baixo: DIV primeiro (um ancestral alcançado no caminho de descida), depois P (o alvo). Com handlers de bubbling, a ordem seria invertida.

Identificando o Elemento Correto

Dentro de um handler, você geralmente precisa saber em qual elemento o evento se originou versus em qual elemento o handler está anexado. Duas propriedades respondem a isso:

  • event.target — o elemento onde o evento se originou (o mais profundo clicado). Permanece o mesmo durante toda a propagação.
  • event.currentTarget — o elemento cujo listener está em execução atualmente. Muda conforme o evento percorre a árvore, e é igual a this dentro de um handler de função regular.
function logTargets(event) {
  console.log("target:", event.target.tagName);
  console.log("currentTarget:", event.currentTarget.tagName);
}
// Imagine this handler is on a <div> and you click a nested <p>:
// target:        P    (where the click happened)
// currentTarget: DIV  (where the listener lives)

event.target é o que torna a delegação de eventos (mostrada abaixo) possível — um handler em um pai pode identificar exatamente qual filho foi clicado.

Controlando a Propagação

JavaScript oferece vários métodos para controlar até onde um evento se propaga.

MétodoEfeito
event.stopPropagation()Impede o evento de continuar para o próximo elemento no caminho (sem mais bubbling/capturing). Handlers no mesmo elemento ainda executam.
event.stopImmediatePropagation()Para a propagação e impede que outros handlers no mesmo elemento executem.
event.preventDefault()Cancela a ação padrão do navegador (ex.: seguir um link). Não para a propagação.

stopPropagation() e preventDefault() são independentes. Parar a propagação não cancela o comportamento padrão, e vice-versa.

Uma Observação sobre Eventos que Não Fazem Bubbling

A maioria dos eventos faz bubbling, mas alguns não fazem — por exemplo focus, blur, mouseenter, mouseleave e load. Para esses eventos, você não pode depender de um handler pai que os capture via bubbling; use os equivalentes com bubbling (focusin/focusout, mouseover/mouseout) ou anexe o listener diretamente. Você sempre pode verificar event.bubbles para confirmar se um determinado evento participa da fase de bubbling.

Exemplos Práticos

Exemplo 1: Parando o Event Bubbling

Às vezes você quer que um clique em um filho não acione o handler do pai:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Event Propagation Example</title>
<style>
  .container {
    width: 200px;
    height: 200px;
    background-color: lightblue;
    padding: 20px;
  }

  .box {
    width: 100px;
    height: 100px;
    background-color: pink;
    margin-top: 20px;
    cursor: pointer;
  }
</style>
</head>
<body>

<div class="container" onclick="alert('You clicked the container!');">
  Click the pink box to see event propagation:
  <div class="box" onclick="event.stopPropagation(); alert('You clicked the box without bubbling!');"></div>
</div>

</body>
</html>

Neste exemplo, há um container com fundo azul claro contendo uma caixa rosa. Clicar em qualquer lugar dentro do container aciona um alerta dizendo "You clicked the container!". No entanto, clicar na caixa rosa aciona um alerta diferente dizendo "You clicked the box without bubbling!" porque event.stopPropagation() impede que o evento de clique suba até o container.

Exemplo 2: Usando Bubbling e Capturing Juntos

Este exemplo mostra como lidar com um evento tanto na fase de capturing quanto na de bubbling:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Event Capture and Bubbling Example</title>
<style>
  body {
    font-family: Arial, sans-serif;
    margin: 0;
    padding: 20px;
  }

  #outerContainer {
    border: 2px solid #ccc;
    padding: 20px;
    margin-bottom: 20px;
    background-color: #f9f9f9;
    border-radius: 10px;
  }

  #innerElement {
    background-color: #ffa8a8;
    padding: 10px;
    border-radius: 5px;
    cursor: pointer;
  }
</style>
</head>
<body>

<div id="outerContainer" onclick="alert('Event Bubbled from Outer Container');">
  <p style="margin: 0;">Click anywhere in this outer container:</p>
  <p id="innerElement">Click me!</p>
</div>

<script>
  // Event listener attached to the outer container during the capturing phase
  document.getElementById('outerContainer').addEventListener('click', function() {
    alert('Event Captured by Outer Container');
  }, true);

  // Event listener attached to the inner element during the bubbling phase
  document.getElementById('innerElement').addEventListener('click', function() {
    alert('Event Bubbled from Inner Element');
  }, false);
</script>

</body>
</html>

Quando você clica no elemento interno:

  1. O listener de capturing em #outerContainer executa primeiro, alertando "Event Captured by Outer Container" (ele está no caminho de descida até o alvo).
  2. O listener de bubbling em #innerElement executa em seguida, alertando "Event Bubbled from Inner Element" (o próprio alvo).
  3. Por fim, o onclick inline em #outerContainer executa conforme o evento sobe, alertando "Event Bubbled from Outer Container".

Isso torna o caminho completo de propagação visível em ordem: capturing descendo, atingindo o alvo e, em seguida, bubbling subindo.

Exemplo 3: Delegação de Eventos

O uso prático mais comum do bubbling é a delegação de eventos — anexar um único listener a um pai em vez de um listener por filho. Como os cliques sobem via bubbling, o pai pode usar event.target para descobrir qual filho foi clicado. Isso é eficiente e funciona automaticamente para elementos adicionados posteriormente.

const list = document.getElementById("menu");

list.addEventListener("click", function (event) {
  // Did the click originate on an <li>?
  const item = event.target.closest("li");
  if (!item || !list.contains(item)) return;

  console.log("You clicked:", item.textContent);
});

Com esse único handler, cada <li> atual e futuro dentro de #menu está coberto — você nunca precisa vincular (ou revincular) listeners em itens individuais. Consulte Tratamento de Eventos no DOM e Disparando Eventos Personalizados para técnicas relacionadas.

Conclusão

A propagação de eventos move um evento para baixo (capturing) e depois para cima (bubbling) pelo DOM. Por padrão, os handlers executam durante o bubbling; passe true (ou { capture: true }) para ouvir durante o capturing. Use event.target para encontrar o elemento que iniciou o evento, event.currentTarget para o elemento que o está tratando, e stopPropagation() / stopImmediatePropagation() para limitar até onde ele se propaga. Domine esses conceitos e você poderá criar interações limpas e eficientes — principalmente por meio da delegação de eventos — sem espalhar listeners por toda a sua página.

Prática

Prática
Qual propriedade indica o elemento onde um evento se originou originalmente, independentemente de qual handler está em execução?
Qual propriedade indica o elemento onde um evento se originou originalmente, independentemente de qual handler está em execução?
Prática
Por padrão, em qual fase os listeners adicionados com addEventListener são executados?
Por padrão, em qual fase os listeners adicionados com addEventListener são executados?
Was this page helpful?