Start · Sprachen · JavaScript · Referenz · MutationObserver

MutationObserver

Schnittstelle

Beobachtet Änderungen im DOM-Baum (Kindknoten, Attribute, Textinhalte) und ruft eine Callback-Funktion mit gesammelten <code>MutationRecord</code>-Objekten auf.

seit JavaScript DOM4 · Baseline: alle Browser (seit 2012/2013) Kategorie: webapi

Signatur

interface MutationObserver

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. In options muss mindestens einer der Werte childList, attributes oder characterData auf true stehen (oder indirekt über attributeOldValue, attributeFilter oder characterDataOldValue gesetzt 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 vor disconnect().

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

Typ
MutationObserver
Beschreibung
Der Konstruktor gibt eine neue Observer-Instanz zurück. 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);
Neu: Hallo Welt

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');
aria-expanded: "false" → "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);
Dialog gefunden: cookie-dialog

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);
1 characterData

// Wichtig · Fallstricke

  • Ungültige Optionen: Ist keiner der Werte childList, attributes oder characterData gesetzt, auch nicht indirekt, wirft observe() einen TypeError. Dasselbe gilt für attributeOldValue oder attributeFilter zusammen mit attributes: 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 wieder observe() aufrufen.
  • Performance: subtree: true auf document oder body kann bei DOM-lastigen Anwendungen sehr viele Datensätze erzeugen. Beobachte deshalb einen möglichst kleinen Teilbaum, nutze attributeFilter und 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 (DOMNodeInserted usw.) werden aus Chrome entfernt und sollten nicht mehr verwendet werden.