W3docs

JavaScript MutationObserver API

Aprenda a API MutationObserver do JavaScript: observe mudanças no DOM, métodos observe/disconnect/takeRecords, MutationRecords e padrões práticos.

A API MutationObserver no JavaScript permite observar uma parte do DOM e executar um callback sempre que algo dentro dela muda — um nó é adicionado ou removido, um atributo é editado ou o conteúdo de texto é atualizado. Ao contrário dos obsoletos mutation events que substituiu, o MutationObserver agrupa as mudanças e as entrega de forma assíncrona, sem bloquear a página nem disparar um evento separado para cada pequena edição.

Este guia aborda o que você pode observar, os três métodos que cada observer expõe, a estrutura de um MutationRecord, casos de uso do mundo real, armadilhas comuns e um exemplo interativo que você pode executar.

Quando usar um MutationObserver?

Você recorre a um MutationObserver quando um conteúdo que você não controla muda e você precisa reagir a isso:

  • Conteúdo de terceiros / injetado — um widget, anúncio ou bloco renderizado por um CMS aparece no DOM e você precisa estilizá-lo ou aprimorá-lo.
  • Detectar quando um elemento finalmente existe — aguardar um nó que um framework renderiza depois, em vez de fazer polling com setInterval.
  • Sincronizar a UI com mudanças de atributo — reagir quando class, style, disabled ou um atributo data-* muda em um elemento ao qual você não pode adicionar um listener.
  • Redimensionamento automático ou recálculo de medidas — recalcular o layout quando elementos filhos são inseridos em um container.
  • Manter um modelo sincronizado com texto de contenteditable.

Se você só precisa saber quando um elemento entra ou sai do viewport, use um IntersectionObserver — ele é específico para isso e mais eficiente.

Os três métodos

Cada instância de observer expõe exatamente três métodos:

MétodoO que faz
observe(target, options)Começa a observar target usando um object de opções. Chame novamente com um target diferente para observar mais de um nó com o mesmo observer.
disconnect()Para de observar todos os targets. O callback não será mais disparado. Sempre chame isso quando terminar para evitar vazamentos de memória.
takeRecords()Retorna de forma síncrona (e limpa) quaisquer MutationRecords pendentes que ainda não foram entregues ao callback. Útil logo antes de disconnect() para não perder o último lote.

O object options passado para observe() deve habilitar pelo menos um de childList, attributes ou characterData, ou a chamada lança um TypeError.

OpçãoSignificado
childListObserva nós filhos diretos adicionados/removidos.
attributesObserva mudanças de atributo.
characterDataObserva mudanças nos dados de nós de texto.
subtreeEstende o acima para todos os descendentes, não apenas o target.
attributeOldValueRegistra o valor anterior do atributo (implica attributes).
characterDataOldValueRegistra o valor de texto anterior (implica characterData).
attributeFilterArray de nomes de atributos a observar — ignora os demais.

Exemplo de Mutation Observer

Veja um exemplo básico para ajudar você a entender como um Mutation Observer funciona, indicando visualmente as mudanças no DOM.

<!DOCTYPE html>
<html>
<head>
  <title>Exploring DOM Changes: Live Examples with Mutation Observers</title>
</head>
<body>
  <div id="target" style="background-color: lightgray; padding: 10px;">
    Watch this space for changes!
  </div>
  <button style="margin-top: 10px;" onclick="addNewElement(); changeAttribute();">Add New Element and Change Color</button>
  <div id="log" style="margin-top: 20px;"></div>

  <script>
    // Get the element to observe
    const targetNode = document.getElementById('target');

    // Define configurations for the observer
    const config = { attributes: true, childList: true, subtree: true, attributeOldValue: true };

    // Callback function to execute when mutations are observed
    const callback = function(mutationsList, observer) {
      for (const mutation of mutationsList) {
        const message = document.createElement('p');
        if (mutation.type === 'childList') {
          message.textContent = 'A child node has been added or removed.';
          message.style.color = 'green';
        } else if (mutation.type === 'attributes') {
          message.textContent = 'The ' + mutation.attributeName + ' attribute was modified.';
          message.style.color = 'blue';
        }
        document.getElementById('log').appendChild(message);
      }
    };

    // Create an observer instance linked to the callback function
    const observer = new MutationObserver(callback);

    // Start observing the target node for configured mutations
    observer.observe(targetNode, config);

    // Function to add new elements
    function addNewElement() {
      const newElement = document.createElement('div');
      newElement.textContent = 'New element added!';
      targetNode.appendChild(newElement);
    }

    // Function to change attributes
    function changeAttribute() {
      const currentColor = targetNode.style.backgroundColor;
      targetNode.style.backgroundColor = currentColor === 'lightgray' ? 'lightblue' : 'lightgray';
    }
  </script>
</body>
</html>

Este exemplo demonstra como usar um Mutation Observer para detectar e reagir a mudanças no Document Object Model (DOM) de uma página. Veja o que cada parte do JavaScript faz e o que você pode esperar ao interagir com o exemplo:

  1. Configurar o Mutation Observer:
    • Nó target: É o elemento do DOM que você deseja observar. Neste caso, é o div com ID target.
    • Configurações: Especificam quais tipos de mudanças você deseja monitorar:
      • attributes: O observer vai procurar mudanças em atributos (como style ou class).
      • childList: Vai verificar adição ou remoção de elementos filhos (como novos divs sendo adicionados).
      • subtree: Garante que o observer verifique não só o elemento target, mas também seus descendentes. Note que subtree só tem efeito se childList ou attributes também estiver habilitado.
      • attributeOldValue: Registra o valor anterior de qualquer atributo modificado (útil para rastrear mudanças).
  2. Definir uma função de callback:
    • Esta função é executada cada vez que o observer detecta uma mudança com base nas configurações definidas.
    • Ela percorre todas as mutações detectadas e cria uma mensagem de log para cada uma:
      • Se um elemento filho for adicionado ou removido, registra "A child node has been added or removed." em texto verde.
      • Se um atributo for alterado (como a cor de fundo), registra "The mutation.attributeName attribute was modified." em texto azul.
  3. Instância do Observer:
    • O Mutation Observer é criado e vinculado à função de callback.
  4. Iniciar a observação:
    • O observer começa a monitorar o div target para quaisquer mudanças especificadas nas configurações.
  5. Funções interativas:
    • Adicionar novo elemento: Acionada por um clique no botão, esta função adiciona um novo div com o texto "New element added!" dentro do div target.
    • Alterar atributo: Também acionada pelo mesmo clique, esta função alterna a cor de fundo do div target entre 'lightgray' e 'lightblue'. Nota: embora o onclick inline funcione para este exemplo, usar addEventListener é recomendado para uma melhor separação de código em produção.

Resultados esperados:

  • Adicionando um novo elemento:
    • A cada clique no botão, um novo div é adicionado. Isso aciona a verificação de childList do observer e você verá uma mensagem verde dizendo "A child node has been added or removed."
  • Alterando um atributo:
    • O mesmo clique no botão altera a cor de fundo do div target. Isso aciona a verificação de atributo do observer. Você verá uma mensagem azul indicando qual atributo foi alterado ("The style attribute was modified.").

Este exemplo demonstra efetivamente como Mutation Observers podem ser usados para monitorar e registrar mudanças no DOM, fornecendo feedback em tempo real sobre o que acontece dentro da página.

Lendo um MutationRecord

O callback recebe um array de objetos MutationRecord — um para cada mudança detectada. As propriedades mais úteis são:

  • type"childList", "attributes" ou "characterData".
  • target — o nó afetado pela mutação.
  • addedNodes / removedNodesNodeLists de nós inseridos/removidos (para childList).
  • attributeName — o atributo alterado (para attributes).
  • oldValue — o valor anterior, mas somente se attributeOldValue ou characterDataOldValue estava habilitado.
const observer = new MutationObserver((records) => {
  for (const record of records) {
    if (record.type === "attributes") {
      console.log(`${record.attributeName} changed from "${record.oldValue}"`);
    } else if (record.type === "childList") {
      console.log(`+${record.addedNodes.length} / -${record.removedNodes.length} nodes`);
    }
  }
});

Um padrão prático: aguardar um elemento aparecer

Um uso comum no mundo real é resolver uma Promise no momento em que um nó aparece no DOM — muito melhor do que fazer polling. O observer desconecta a si mesmo assim que encontra o elemento:

function waitForElement(selector) {
  return new Promise((resolve) => {
    const existing = document.querySelector(selector);
    if (existing) return resolve(existing);

    const observer = new MutationObserver(() => {
      const el = document.querySelector(selector);
      if (el) {
        observer.disconnect(); // stop watching once found
        resolve(el);
      }
    });

    observer.observe(document.body, { childList: true, subtree: true });
  });
}

// Usage with async/await:
// const card = await waitForElement(".lazy-card");

Isso se combina naturalmente com async/await e Promises.

Armadilhas e boas práticas

  • O callback é assíncrono e em lote. As mutações são enfileiradas como microtasks e entregues após o script atual terminar — veja microtasks e o event loop. Você não receberá um record para cada mudança individual; eles chegam agrupados.
  • Seu callback roda depois da mudança. Você é notificado do que já aconteceu — não é possível cancelar ou impedir uma mutação como faria com preventDefault() em um evento.
  • oldValue é opt-in. Se você ler record.oldValue sem habilitar attributeOldValue/characterDataOldValue, o valor será null.
  • Evite modificar o subtree observado dentro do callback a menos que seja intencional — fazer isso pode gerar mais records e criar um loop de feedback.
  • Sempre chame disconnect(). Um observer ativo mantém seu target alcançável, então esquecer de desconectar vaza memória. Desconecte quando o componente desmontar ou o trabalho estiver concluído.
  • Use takeRecords() antes de desconectar se o último lote for importante — disconnect() descarta records não entregues.

Conclusão

Mutation Observers são uma parte essencial do conjunto de ferramentas do JavaScript, oferecendo soluções dinâmicas para gerenciar mudanças no DOM de forma eficiente. Eles capacitam desenvolvedores a construir aplicações web responsivas e interativas que reagem de forma fluida a interações do usuário e modificações programáticas no DOM. Embora poderosos, é essencial usar Mutation Observers com critério para manter desempenho e experiência do usuário ideais. Ao selecionar cuidadosamente quais mutações observar, minimizar o overhead nos callbacks de mutação e chamar observer.disconnect() quando o observer não for mais necessário para evitar vazamentos de memória, os desenvolvedores podem aproveitar Mutation Observers para aprimorar a funcionalidade do site sem comprometer a eficiência. Compreender e aplicar esses princípios permite criar interfaces web avançadas e amigáveis que se destacam no cenário digital moderno.

Prática

Prática
Quais das afirmações a seguir são verdadeiras sobre o JavaScript Mutation Observer?
Quais das afirmações a seguir são verdadeiras sobre o JavaScript Mutation Observer?
Was this page helpful?