W3docs

JavaScript MutationObserver API

Die JavaScript MutationObserver API: DOM-Änderungen beobachten, Methoden, MutationRecords lesen, praxisnahe Muster und ein ausführbares Beispiel.

Die MutationObserver-API in JavaScript ermöglicht es, einen Bereich des DOM zu beobachten und bei jeder Änderung darin eine Callback-Funktion auszuführen — wenn ein Knoten hinzugefügt oder entfernt wird, ein Attribut geändert wird oder Textinhalt aktualisiert wird. Im Gegensatz zu den veralteten Mutation Events, die sie ersetzt hat, fasst MutationObserver Änderungen zusammen und liefert sie asynchron, sodass die Seite nicht blockiert wird und kein separates Ereignis für jede kleine Änderung ausgelöst wird.

Dieses Handbuch behandelt, was beobachtet werden kann, die drei Methoden jeder Observer-Instanz, die Struktur eines MutationRecord, praxisnahe Anwendungsfälle, häufige Fallstricke und ein interaktives Beispiel zum Ausprobieren.

Wann würde man einen MutationObserver verwenden?

Ein MutationObserver kommt zum Einsatz, wenn sich Inhalte, die man nicht kontrolliert, ändern und man darauf reagieren muss:

  • Drittanbieter- / eingebettete Inhalte — ein Widget, eine Werbeanzeige oder ein CMS-gerenderter Block erscheint im DOM und muss gestaltet oder erweitert werden.
  • Erkennen, wann ein Element endlich vorhanden ist — auf einen Knoten warten, den ein Framework später rendert, anstatt mit setInterval zu pollen.
  • UI mit Attributänderungen synchronisieren — reagieren, wenn sich class, style, disabled oder ein data-*-Attribut an einem Element ändert, dem kein Listener hinzugefügt werden kann.
  • Automatische Größenanpassung oder Neuberechnung — das Layout neu berechnen, wenn Kindelemente in einen Container eingefügt werden.
  • Ein Modell mit dem Text eines contenteditable-Elements synchron halten.

Wenn nur bekannt sein muss, wann ein Element den Viewport betritt oder verlässt, sollte stattdessen ein IntersectionObserver verwendet werden — er ist speziell dafür entwickelt und günstiger.

Die drei Methoden

Jede Observer-Instanz bietet genau drei Methoden:

MethodeWas sie tut
observe(target, options)Beginnt, target anhand eines Options-Objekts zu beobachten. Erneuter Aufruf mit einem anderen Ziel ermöglicht das Beobachten mehrerer Knoten mit demselben Observer.
disconnect()Stoppt die Beobachtung aller Ziele. Der Callback wird nicht mehr ausgelöst. Immer aufrufen, wenn die Arbeit abgeschlossen ist, um Memory Leaks zu vermeiden.
takeRecords()Gibt synchron alle ausstehenden MutationRecords zurück (und löscht sie), die noch nicht an den Callback geliefert wurden. Nützlich direkt vor disconnect(), damit der letzte Batch nicht verloren geht.

Das options-Objekt, das an observe() übergeben wird, muss mindestens eine der Optionen childList, attributes oder characterData aktivieren, andernfalls wird ein TypeError geworfen.

OptionBedeutung
childListDirekt hinzugefügte oder entfernte Kindknoten beobachten.
attributesAttributänderungen beobachten.
characterDataÄnderungen an Textknoten-Daten beobachten.
subtreeDie obigen Optionen auf alle Nachkommen ausweiten, nicht nur auf das Ziel.
attributeOldValueDen vorherigen Attributwert aufzeichnen (impliziert attributes).
characterDataOldValueDen vorherigen Textwert aufzeichnen (impliziert characterData).
attributeFilterArray von zu beobachtenden Attributnamen — alle anderen werden ignoriert.

Mutation Observer Beispiel

Hier ist ein einfaches Beispiel, das zeigt, wie ein Mutation Observer funktioniert, indem es Änderungen im DOM visuell anzeigt.

<!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>

Dieses Beispiel veranschaulicht, wie ein Mutation Observer eingesetzt wird, um Änderungen im Document Object Model (DOM) einer Webseite zu erkennen und darauf zu reagieren. Hier wird erklärt, was jeder Teil des JavaScript-Codes tut und was bei der Interaktion mit dem Beispiel zu erwarten ist:

  1. Mutation Observer einrichten:
    • Zielknoten: Das ist das DOM-Element, das beobachtet werden soll. In diesem Fall ist es das div mit der ID target.
    • Konfigurationen: Diese legen fest, welche Arten von Änderungen überwacht werden sollen:
      • attributes: Der Observer sucht nach Änderungen an Attributen (wie style oder class).
      • childList: Er prüft, ob Kindelemente hinzugefügt oder entfernt werden (z. B. neue div-Elemente).
      • subtree: Dadurch überprüft der Observer nicht nur das Zielelement, sondern auch dessen Nachkommen. subtree wirkt nur, wenn auch childList oder attributes aktiviert ist.
      • attributeOldValue: Zeichnet den vorherigen Wert eines geänderten Attributs auf (nützlich zum Verfolgen von Änderungen).
  2. Eine Callback-Funktion definieren:
    • Diese Funktion wird jedes Mal ausgeführt, wenn der Observer basierend auf der Konfiguration eine Änderung erkennt.
    • Sie durchläuft alle erkannten Mutationen und erstellt für jede eine Protokollmeldung:
      • Wenn ein Kindelement hinzugefügt oder entfernt wurde, wird "A child node has been added or removed." in grünem Text protokolliert.
      • Wenn ein Attribut geändert wurde (z. B. die Hintergrundfarbe), wird "The mutation.attributeName attribute was modified." in blauem Text protokolliert.
  3. Observer-Instanz:
    • Der Mutation Observer wird erstellt und mit der Callback-Funktion verknüpft.
  4. Beobachtung starten:
    • Der Observer beginnt, das target-div auf alle in der Konfiguration festgelegten Änderungen zu überwachen.
  5. Interaktive Funktionen:
    • Neues Element hinzufügen: Durch einen Button-Klick ausgelöst, fügt diese Funktion ein neues div mit dem Text "New element added!" innerhalb des target-div hinzu.
    • Attribut ändern: Ebenfalls durch denselben Button-Klick ausgelöst, wechselt diese Funktion die Hintergrundfarbe des target-div zwischen 'lightgray' und 'lightblue'. Hinweis: Auch wenn das inline onclick in diesem Beispiel funktioniert, wird für sauberere Code-Trennung in der Produktion addEventListener empfohlen.

Erwartete Ergebnisse:

  • Ein neues Element hinzufügen:
    • Jedes Mal, wenn der Button geklickt wird, wird ein neues div hinzugefügt. Dies löst die childList-Prüfung des Observers aus, und es wird eine grüne Meldung "A child node has been added or removed." angezeigt.
  • Ein Attribut ändern:
    • Derselbe Button-Klick ändert die Hintergrundfarbe des target-div. Dies löst die Attributprüfung des Observers aus. Es wird eine blaue Meldung angezeigt, die angibt, welches Attribut geändert wurde ("The style attribute was modified.").

Dieses Beispiel demonstriert effektiv, wie Mutation Observer eingesetzt werden können, um Änderungen im DOM zu überwachen und zu protokollieren, und liefert dabei Echtzeit-Feedback über das Geschehen auf der Webseite.

Ein MutationRecord lesen

Der Callback erhält ein Array von MutationRecord-Objekten — eines pro erkannter Änderung. Die nützlichsten Eigenschaften sind:

  • type"childList", "attributes" oder "characterData".
  • target — der Knoten, den die Mutation betroffen hat.
  • addedNodes / removedNodesNodeLists der eingefügten/entfernten Knoten (für childList).
  • attributeName — das geänderte Attribut (für attributes).
  • oldValue — der vorherige Wert, aber nur, wenn attributeOldValue oder characterDataOldValue aktiviert war.
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`);
    }
  }
});

Ein praktisches Muster: auf das Erscheinen eines Elements warten

Ein häufiger Anwendungsfall in der Praxis ist das Auflösen eines Promise in dem Moment, in dem ein Knoten im DOM erscheint — weit besser als Polling. Der Observer trennt sich selbst, sobald er das Element gefunden hat:

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");

Dies lässt sich gut mit async/await und Promises kombinieren.

Fallstricke und Best Practices

  • Der Callback ist asynchron und gebündelt. Mutationen werden als Microtasks in die Warteschlange eingereiht und nach Abschluss des aktuellen Skripts geliefert — siehe Microtasks und die Event Loop. Es gibt keinen Datensatz für jede einzelne Änderung; sie werden gruppiert geliefert.
  • Der Callback wird nach der Änderung ausgeführt. Es wird über das benachrichtigt, was bereits passiert ist — eine Mutation kann nicht abgebrochen oder verhindert werden, wie es mit preventDefault() bei einem Event möglich wäre.
  • oldValue muss explizit aktiviert werden. Wenn record.oldValue gelesen wird, ohne dass attributeOldValue/characterDataOldValue aktiviert wurde, ist der Wert null.
  • Das beobachtete Subtree im Callback nicht mutieren, es sei denn, das ist beabsichtigt — dies kann weitere Datensätze auslösen und eine Rückkopplungsschleife erzeugen.
  • Immer disconnect() aufrufen. Ein aktiver Observer hält sein Ziel erreichbar, sodass das Vergessen des Trennens zu Memory Leaks führt. Trennen, wenn die Komponente ausgehängt wird oder die Arbeit abgeschlossen ist.
  • takeRecords() vor dem Trennen aufrufen, wenn der letzte Batch wichtig ist — disconnect() verwirft nicht gelieferte Datensätze.

Fazit

Mutation Observer sind ein wichtiger Bestandteil des JavaScript-Werkzeugkastens und bieten dynamische Lösungen für die effiziente Verwaltung von DOM-Änderungen. Sie ermöglichen Entwicklern, reaktionsfähige, interaktive Webanwendungen zu erstellen, die nahtlos auf Benutzerinteraktionen und programmatische DOM-Modifikationen reagieren. Obwohl sie leistungsfähig sind, ist es wichtig, Mutation Observer umsichtig einzusetzen, um optimale Performance und Benutzererfahrung aufrechtzuerhalten. Durch sorgfältige Auswahl der zu beobachtenden Mutationen, Minimierung des Aufwands in Mutation-Callbacks und den Aufruf von observer.disconnect(), wenn der Observer nicht mehr benötigt wird, können Entwickler Mutation Observer nutzen, um die Funktionalität der Seite zu verbessern, ohne die Effizienz zu beeinträchtigen. Das Verstehen und Anwenden dieser Prinzipien ermöglicht die Erstellung fortschrittlicher, benutzerfreundlicher Web-Interfaces, die in der modernen digitalen Landschaft herausstechen.

Übungen

Übung
Welche der folgenden Aussagen über den JavaScript Mutation Observer sind korrekt?
Welche der folgenden Aussagen über den JavaScript Mutation Observer sind korrekt?
Was this page helpful?