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,disabledou um atributodata-*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étodo | O 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ção | Significado |
|---|---|
childList | Observa nós filhos diretos adicionados/removidos. |
attributes | Observa mudanças de atributo. |
characterData | Observa mudanças nos dados de nós de texto. |
subtree | Estende o acima para todos os descendentes, não apenas o target. |
attributeOldValue | Registra o valor anterior do atributo (implica attributes). |
characterDataOldValue | Registra o valor de texto anterior (implica characterData). |
attributeFilter | Array 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:
- Configurar o Mutation Observer:
- Nó target: É o elemento do DOM que você deseja observar. Neste caso, é o
divcom IDtarget. - 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 quesubtreesó tem efeito sechildListouattributestambém estiver habilitado.attributeOldValue: Registra o valor anterior de qualquer atributo modificado (útil para rastrear mudanças).
- Nó target: É o elemento do DOM que você deseja observar. Neste caso, é o
- 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.attributeNameattribute was modified." em texto azul.
- Instância do Observer:
- O Mutation Observer é criado e vinculado à função de callback.
- Iniciar a observação:
- O observer começa a monitorar o
divtargetpara quaisquer mudanças especificadas nas configurações.
- O observer começa a monitorar o
- 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
divtarget. - Alterar atributo: Também acionada pelo mesmo clique, esta função alterna a cor de fundo do
divtargetentre 'lightgray' e 'lightblue'. Nota: embora oonclickinline funcione para este exemplo, usaraddEventListeneré recomendado para uma melhor separação de código em produção.
- 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
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
divtarget. Isso aciona a verificação de atributo do observer. Você verá uma mensagem azul indicando qual atributo foi alterado ("The style attribute was modified.").
- O mesmo clique no botão altera a cor de fundo do
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/removedNodes—NodeLists de nós inseridos/removidos (parachildList).attributeName— o atributo alterado (paraattributes).oldValue— o valor anterior, mas somente seattributeOldValueoucharacterDataOldValueestava 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ê lerrecord.oldValuesem habilitarattributeOldValue/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.