Signatur
Beschreibung
MutationObserver meldet Änderungen am DOM: neue oder entfernte Kindknoten, geänderte Attribute und geänderte Textinhalte (characterData). Er ersetzt die veralteten, langsamen Mutation Events wie DOMNodeInserted. Die Callback-Funktion läuft nicht synchron bei jeder einzelnen Änderung. Der Browser sammelt die Änderungen und übergibt sie gebündelt als Array von MutationRecord-Objekten in einer Microtask.
Typische Einsatzfälle sind Code, der auf DOM-Änderungen durch Fremdcode reagieren muss (Widgets, Werbeskripte, Frameworks), Lazy-Initialisierung von nachträglich eingefügten Elementen, Browser-Erweiterungen, Undo-Funktionen in Editoren mit contenteditable und Tests, die auf das Erscheinen eines Elements warten.
Die Schnittstelle hat drei Methoden:
observe(target, options)– startet die Beobachtung eines Knotens. Inoptionsmuss mindestens einer der WertechildList,attributesodercharacterDataauftruestehen (oder indirekt überattributeOldValue,attributeFilterodercharacterDataOldValuegesetzt sein).disconnect()– beendet alle Beobachtungen dieses Observers. Noch nicht zugestellte Datensätze werden verworfen.takeRecords()– gibt die noch nicht zugestellten Datensätze zurück und leert die Warteschlange. Das ist nützlich direkt vordisconnect().
Die Parameter unten gehören zum Konstruktor new MutationObserver(callback) und zu observe(target, options).
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $callback Pflicht | (mutations: MutationRecord[], observer: MutationObserver) => void | Konstruktor-Parameter. Wird mit einem Array von MutationRecord-Objekten und dem Observer selbst aufgerufen. |
|
| $target Pflicht | Node | Parameter von observe(): der zu beobachtende Knoten, zum Beispiel ein Element oder document. |
|
| $options.childList | boolean | false | Meldet hinzugefügte und entfernte Kindknoten. |
| $options.subtree | boolean | false | Erweitert die Beobachtung auf alle Nachfahren von target. |
| $options.attributes | boolean | false (true, wenn attributeFilter oder attributeOldValue gesetzt ist) | Meldet Änderungen an Attributen. |
| $options.attributeFilter | string[] | Beschränkt die Beobachtung auf die angegebenen Attributnamen, zum Beispiel ['class', 'hidden']. |
|
| $options.attributeOldValue | boolean | false | Speichert den vorherigen Attributwert in record.oldValue. |
| $options.characterData | boolean | false (true, wenn characterDataOldValue gesetzt ist) | Meldet Änderungen am Inhalt von Text- und Kommentarknoten. |
| $options.characterDataOldValue | boolean | false | Speichert den vorherigen Textinhalt in record.oldValue. |
Rückgabewert
observe() und disconnect() geben undefined zurück, takeRecords() ein MutationRecord[].Beispiele
Nachträglich eingefügte Elemente erkennen
const liste = document.querySelector('#nachrichten');
const observer = new MutationObserver((mutations) => {
for (const m of mutations) {
for (const node of m.addedNodes) {
// Nur Elementknoten verarbeiten, Textknoten überspringen
if (node.nodeType === Node.ELEMENT_NODE) {
console.log('Neu:', node.textContent);
}
}
}
});
observer.observe(liste, { childList: true, subtree: true });
// Löst den Callback aus
const li = document.createElement('li');
li.textContent = 'Hallo Welt';
liste.append(li);
Attributänderungen mit altem Wert verfolgen
const button = document.querySelector('#toggle');
const observer = new MutationObserver((mutations) => {
for (const { attributeName, oldValue, target } of mutations) {
console.log(`${attributeName}: "${oldValue}" → "${target.getAttribute(attributeName)}"`);
}
});
observer.observe(button, {
attributeFilter: ['aria-expanded', 'class'],
attributeOldValue: true
});
button.setAttribute('aria-expanded', 'true');
Auf ein Element warten (Promise-Helfer mit Timeout)
const waitForElement = (selector, { root = document, timeout = 5000 } = {}) =>
new Promise((resolve, reject) => {
const vorhanden = root.querySelector(selector);
if (vorhanden) return resolve(vorhanden);
const observer = new MutationObserver(() => {
const el = root.querySelector(selector);
if (el) {
observer.disconnect();
clearTimeout(timer);
resolve(el);
}
});
const timer = setTimeout(() => {
observer.disconnect();
reject(new Error(`Timeout: ${selector} nicht gefunden`));
}, timeout);
observer.observe(root, { childList: true, subtree: true });
});
const dialog = await waitForElement('.cookie-dialog');
console.log('Dialog gefunden:', dialog.className);
Offene Datensätze vor dem Beenden verarbeiten
const editor = document.querySelector('[contenteditable]');
const verlauf = [];
const observer = new MutationObserver((mutations) => verlauf.push(...mutations));
observer.observe(editor, { characterData: true, characterDataOldValue: true, subtree: true });
editor.firstChild.data = 'Geänderter Text';
// Die Microtask ist noch nicht gelaufen, deshalb Datensätze manuell abholen
verlauf.push(...observer.takeRecords());
observer.disconnect();
console.log(verlauf.length, verlauf[0].type);
// Wichtig · Fallstricke
- Ungültige Optionen: Ist keiner der Werte
childList,attributesodercharacterDatagesetzt, auch nicht indirekt, wirftobserve()einenTypeError. Dasselbe gilt fürattributeOldValueoderattributeFilterzusammen mitattributes: false. - Endlosschleifen: Ändert der Callback selbst das beobachtete DOM, wird er erneut ausgelöst. Deshalb Änderungen nur unter einer Bedingung ausführen oder vorher
disconnect()und danach wiederobserve()aufrufen. - Performance:
subtree: trueaufdocumentoderbodykann bei DOM-lastigen Anwendungen sehr viele Datensätze erzeugen. Beobachte deshalb einen möglichst kleinen Teilbaum, nutzeattributeFilterund halte den Callback schlank. - Asynchron: Der Callback läuft als Microtask und nicht sofort nach der Änderung. Wer synchron Ergebnisse braucht, verwendet
takeRecords(). - Kein Speicherleck durch das Ziel: Der Observer hält nur schwache Referenzen auf die beobachteten Knoten. Wird ein Knoten entfernt, endet seine Beobachtung trotzdem nicht automatisch, solange er noch referenziert ist. Deshalb bei Komponenten-Abbau
disconnect()aufrufen. - Ein
observe()-Aufruf auf denselben Knoten ersetzt die bisherigen Optionen für diesen Knoten. Er fügt keine zweite Beobachtung hinzu. - Shadow DOM: Änderungen innerhalb eines Shadow-Roots werden nur gemeldet, wenn der Shadow-Root selbst beobachtet wird.
- Kompatibilität: Wird von allen modernen Browsern unterstützt, ab Internet Explorer 11. Die veralteten Mutation Events (
DOMNodeInsertedusw.) werden aus Chrome entfernt und sollten nicht mehr verwendet werden.