Signatur
Beschreibung
Der Konstruktor MutationObserver() erzeugt einen Beobachter für Änderungen am DOM-Baum. Direkt nach dem Erzeugen beobachtet er aber noch nichts: Erst wenn Sie observe(target, options) aufrufen, legen Sie fest, welcher Knoten beobachtet wird und welche Art von Änderung zählt (Kindknoten, Attribute, Textinhalt, optional mit dem ganzen Teilbaum).
Der Callback läuft asynchron als Microtask. Änderungen werden gesammelt und gebündelt übergeben, also als Array von MutationRecord-Objekten. Selbst wenn Sie in einer Schleife hundert Knoten einfügen, läuft der Callback deshalb in der Regel nur einmal. Das ist deutlich effizienter als die veralteten Mutation Events (DOMNodeInserted usw.), die synchron und einzeln ausgelöst wurden.
Typische Einsatzfälle sind: auf dynamisch nachgeladene Inhalte reagieren (z. B. in Single-Page-Apps oder Fremd-Widgets), Attributänderungen verfolgen (etwa class oder aria-*), Inhalte in contenteditable-Bereichen überwachen oder warten, bis ein bestimmtes Element im DOM auftaucht.
Eine Instanz kann mehrere Ziele gleichzeitig beobachten. Mit disconnect() beenden Sie alle Beobachtungen. takeRecords() liefert Änderungen, die noch nicht an den Callback übergeben wurden.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $callback Pflicht | (mutations: MutationRecord[], observer: MutationObserver) => void | Funktion, die nach DOM-Änderungen aufgerufen wird. Sie erhält ein Array von MutationRecord-Objekten (mit type, target, addedNodes, removedNodes, attributeName, oldValue usw.) und als zweites Argument den Beobachter selbst. Damit kann der Callback z. B. observer.disconnect() aufrufen. |
Rückgabewert
MutationObserver-Objekt. Es beobachtet erst nach einem Aufruf von observe() etwas.Beispiele
Neu eingefügte Kindelemente einer Liste protokollieren
const list = document.querySelector('#todo-list');
const observer = new MutationObserver((mutations) => {
for (const m of mutations) {
if (m.type === 'childList') {
m.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
console.log('Hinzugefügt:', node.textContent);
}
});
}
}
});
observer.observe(list, { childList: true });
// Änderungen auslösen
const li = document.createElement('li');
li.textContent = 'Milch kaufen';
list.append(li);
Attributänderungen mit altem Wert verfolgen
const button = document.querySelector('#theme-toggle');
const observer = new MutationObserver((mutations) => {
for (const { attributeName, oldValue, target } of mutations) {
console.log(`${attributeName}: "${oldValue}" → "${target.getAttribute(attributeName)}"`);
}
});
observer.observe(button, {
attributes: true,
attributeFilter: ['class', 'aria-pressed'],
attributeOldValue: true
});
button.setAttribute('aria-pressed', 'true');
Auf ein Element warten (Promise-Helfer)
const waitForElement = (selector, root = document.body) =>
new Promise((resolve) => {
const existing = root.querySelector(selector);
if (existing) return resolve(existing);
const observer = new MutationObserver((_, obs) => {
const el = root.querySelector(selector);
if (el) {
obs.disconnect(); // Beobachtung beenden
resolve(el);
}
});
observer.observe(root, { childList: true, subtree: true });
});
const dialog = await waitForElement('.cookie-banner');
console.log('Banner gefunden:', dialog.className);
// Wichtig · Fallstricke
- Asynchron: Der Callback läuft als Microtask und nicht sofort nach der Änderung. Code direkt nach einer DOM-Manipulation sieht den Callback also noch nicht. Mit
takeRecords()holen Sie ausstehende Datensätze synchron ab. - Endlosschleifen: Wenn der Callback selbst das beobachtete DOM ändert, löst er sich erneut aus. Rufen Sie in dem Fall vorher
disconnect()auf oder prüfen Sie die Bedingung sorgfältig. - Performance:
subtree: trueaufdocument.bodykann bei großen, sehr dynamischen Seiten teuer werden. Beobachten Sie möglichst gezielt und nutzen SieattributeFilter. - Mindestens eine der Optionen
childList,attributesodercharacterDatamuss gesetzt sein, sonst wirftobserve()einenTypeError. Wenn SieattributeOldValueoderattributeFilterangeben, giltattributes: trueautomatisch. - Der Konstruktor muss mit
newaufgerufen werden. Ohnenewgibt es einenTypeError. - Beobachter nach Gebrauch mit
disconnect()beenden, damit keine unnötige Arbeit anfällt und keine Speicherlecks entstehen. - Kompatibilität: Alle modernen Browser unterstützen die API (ab IE 11). Die veralteten Mutation Events sind abgekündigt und sollten nicht mehr verwendet werden.