W3docs

JavaScript — Disparando Eventos Personalizados

Aprenda a criar e disparar eventos personalizados em JavaScript com CustomEvent e dispatchEvent, passar dados pelo detail e desacoplar componentes.

Disparando Eventos Personalizados em JavaScript

O navegador dispara eventos nativos como click, submit e keydown automaticamente. Mas o JavaScript também permite que você crie e dispare seus próprios eventos em qualquer nó do DOM, e os escute com o mesmo addEventListener() que você já usa para eventos nativos.

Esta é a base do design de aplicações fracamente acopladas: uma parte do seu código anuncia que algo aconteceu ("dados carregados", "carrinho atualizado") sem saber nem se importar com quem está ouvindo. Esta página explica como criar eventos personalizados com o construtor CustomEvent, anexar dados a eles, dispará-los com dispatchEvent() e lidar com os problemas mais comuns.

Criando um evento personalizado

Use o construtor CustomEvent. O primeiro argumento é o tipo do evento (o nome que você vai escutar depois); o segundo é um object de opções:

let event = new CustomEvent("myEvent", {
  detail: { message: "This is a custom event!" },
  bubbles: true,
  cancelable: true
});

As três opções que você vai usar com mais frequência são:

  • detail — qualquer valor (object, string, número) que você queira anexar ao evento. O listener o lê como event.detail. Este é o canal dedicado para dados personalizados; as propriedades de eventos nativos são somente leitura.
  • bubbles — quando true, o evento sobe pelos elementos ancestrais após ser disparado, então um listener em um elemento pai (ou em document) pode capturá-lo. O padrão é false.
  • cancelable — quando true, um listener pode chamar event.preventDefault() para indicar que a ação padrão deve ser ignorada. O padrão é false.

CustomEvent vs. Event

Existe também um construtor Event simples, mas ele não pode carregar dados — não tem detail. Use sempre CustomEvent quando precisar passar informações junto com o evento:

// No way to attach data here:
let bare = new Event("ping", { bubbles: true });

// Use CustomEvent to send a payload:
let withData = new CustomEvent("ping", {
  bubbles: true,
  detail: { at: Date.now() }
});

Disparando o evento

Um evento criado não faz nada até que você o dispare em um elemento com dispatchEvent():

let target = document.getElementById("box");
target.dispatchEvent(event);

Dois pontos importantes sobre dispatchEvent():

  1. Ele é executado de forma síncrona — ao contrário de setTimeout, os listeners são executados imediatamente, antes que a linha após dispatchEvent() seja executada.
  2. Ele retorna um boolean: false se o evento era cancelable e um listener chamou preventDefault(), caso contrário true. Isso permite que o código que disparou o evento reaja a um "veto":
let cancelled = !target.dispatchEvent(event);
if (cancelled) {
  console.log("A listener prevented the default action.");
}

Você pode disparar em qualquer elemento. Disparar em document funciona porque eventos com bubbling o alcançam, mas disparar no elemento específico que "possui" o evento (e deixá-lo subir) mantém seus listeners com escopo definido e evita handlers globais acidentais.

Escutando um evento personalizado

Não há uma API especial do lado do listener — addEventListener() funciona exatamente como para click. Os dados chegam em event.detail:

element.addEventListener("myEvent", function (event) {
  console.log(event.detail.message); // "This is a custom event!"
});

Exemplos práticos

Exemplo 1: Comunicação entre componentes

Suponha que duas partes independentes de uma página precisem se comunicar sem ter referências diretas uma para a outra. Uma dispara um evento personalizado; a outra escuta. Como o evento usa bubbles, o listener pode ficar em document:

<button id="sender">Send Message</button>
// Listener in another component
document.addEventListener('componentMessage', function(event) {
  alert('Received message: ' + event.detail.message);
});

document.getElementById('sender').addEventListener('click', function() {
  // Create and dispatch the custom event
  let customEvent = new CustomEvent('componentMessage', {
    detail: { message: 'Hello from another component!' },
    bubbles: true,
    cancelable: true
  });
  document.dispatchEvent(customEvent);
});

Como funciona:

  • Clicar no botão cria e dispara um evento componentMessage carregando uma mensagem em detail.
  • Um listener em outro lugar captura o evento e reage a ele. Nenhum dos lados importa o outro — o nome do evento é o único contrato.

Exemplo 2: Atualizando a interface após uma mudança de dados

Eventos personalizados permitem separar a lógica de dados da lógica de renderização. A camada de dados anuncia dataUpdated; a camada de UI escuta e re-renderiza. Aqui a atualização é acionada após um pequeno atraso para simular uma requisição assíncrona:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Custom Event UI Update Example</title>
</head>
<body>
<h1>User Status</h1>
<div id="userInfo">Loading user information...</div>

<script>
  // Function to simulate a data update
  function updateData() {
    let dataUpdateEvent = new CustomEvent('dataUpdated', {
      detail: { data: { username: 'user123', status: 'active' } }
    });
    document.dispatchEvent(dataUpdateEvent);
  }

  // UI component listening for data updates
  document.addEventListener('dataUpdated', function(event) {
    let userData = event.detail.data;
    document.getElementById('userInfo').innerHTML =
      `Username: <strong>${userData.username}</strong>, Status: <strong>${userData.status}</strong>`;
  });

  // Trigger the update after 2 seconds
  setTimeout(updateData, 2000);
</script>
</body>
</html>

Como funciona:

  • Após dois segundos, updateData() dispara um evento dataUpdated com os novos dados em detail.
  • O listener de UI lê event.detail.data e atualiza a página. A função que produziu os dados nunca toca o DOM diretamente.

Cancelando um evento personalizado

Quando um evento é cancelable, um listener pode chamar preventDefault(). O dispatcher então vê um retorno false de dispatchEvent() e pode ignorar seu comportamento padrão — o mesmo padrão que o navegador usa para envio de formulários ou cliques em links. Este trecho executa no Node e demonstra o fluxo síncrono:

let target = new EventTarget();

target.addEventListener("save", (event) => {
  // Veto the save
  event.preventDefault();
});

let event = new CustomEvent("save", { cancelable: true });
let notCancelled = target.dispatchEvent(event);

console.log(notCancelled);          // false
console.log(event.defaultPrevented); // true

EventTarget é a interface base que os nós do DOM herdam, então a mesma lógica se aplica tanto ao disparar em um elemento no navegador quanto em um EventTarget independente.

Conclusão

Eventos personalizados oferecem uma maneira limpa e sem dependências de framework para conectar partes de uma aplicação. Crie-os com CustomEvent, anexe um payload via detail, dispare-os com dispatchEvent() e escute com o familiar addEventListener(). Defina bubbles: true quando um elemento pai precisar capturar o evento, e cancelable: true quando o dispatcher precisar respeitar o veto de um listener. O resultado é um código onde os módulos se comunicam por meio de eventos nomeados em vez de referências diretas — mais fácil de testar, estender e manter.

Para aprofundar, veja como os eventos percorrem o DOM em bubbling e capturing, como os listeners são anexados em tratamento de eventos no DOM, e como preventDefault() afeta as ações padrão do navegador.

Prática

Prática
Quais afirmações sobre eventos personalizados em JavaScript estão corretas?
Quais afirmações sobre eventos personalizados em JavaScript estão corretas?
Was this page helpful?