Start · Sprachen · JavaScript · Referenz · MutationRecord.addedNodes

MutationRecord.addedNodes

Eigenschaft

Nur lesbare <code>NodeList</code> mit den Knoten, die bei einer von einem <code>MutationObserver</code> erfassten <code>childList</code>-Mutation eingefügt wurden.

seit JavaScript Baseline: alle Browser (seit 2015) Kategorie: webapi

Signatur

MutationRecord.addedNodes

Beschreibung

Die Eigenschaft addedNodes eines MutationRecord enthält alle Knoten, die durch eine einzelne Mutation als Kinder in den Zielknoten (record.target) eingefügt wurden. Gefüllt ist sie nur bei Datensätzen vom Typ "childList". Bei "attributes" und "characterData" ist sie eine leere NodeList.

Typische Einsatzfälle: Sie wollen dynamisch nachgeladene Inhalte erkennen und verarbeiten, zum Beispiel Widgets initialisieren, Bilder für Lazy-Loading anmelden, Inhalte von Drittanbieter-Skripten überwachen oder in einer Single-Page-App auf neu gerenderte Komponenten reagieren. Mit subtree: true meldet der Observer auch Einfügungen in tieferen Ebenen. Pro Datensatz ist dann target das jeweilige direkte Elternelement.

Wichtig: In der Liste stehen nur die direkt eingefügten Knoten, nicht deren Nachfahren. Fügen Sie ein <div> mit verschachtelten Elementen ein, erscheint nur das <div> selbst. Die Nachfahren müssen Sie mit querySelectorAll() selbst durchsuchen. Neben Elementen können auch Text- und Kommentarknoten enthalten sein. Filtern Sie deshalb bei Bedarf nach node.nodeType === Node.ELEMENT_NODE.

Rückgabewert

Typ
NodeList
Beschreibung
Eine statische NodeList der eingefügten Knoten. Sie ist leer, wenn keine Knoten eingefügt wurden oder der Datensatz nicht vom Typ "childList" ist.

Beispiele

Neu eingefügte Listeneinträge protokollieren

const list = document.createElement('ul');
document.body.append(list);

const observer = new MutationObserver((records) => {
  for (const record of records) {
    for (const node of record.addedNodes) {
      if (node.nodeType === Node.ELEMENT_NODE) {
        console.log('Hinzugefügt:', node.tagName, node.textContent);
      }
    }
  }
});

observer.observe(list, { childList: true });

const li = document.createElement('li');
li.textContent = 'Erster Eintrag';
list.append(li);
Hinzugefügt: LI Erster Eintrag

Dynamisch nachgeladene Widgets samt Nachfahren initialisieren

const initWidget = (el) => {
  if (el.dataset.ready) return;
  el.dataset.ready = 'true';
  console.log('Widget initialisiert:', el.id);
};

const observer = new MutationObserver((records) => {
  for (const { addedNodes } of records) {
    for (const node of addedNodes) {
      if (!(node instanceof Element)) continue;
      // Der Knoten selbst ...
      if (node.matches('[data-widget]')) initWidget(node);
      // ... und alle verschachtelten Widgets
      node.querySelectorAll('[data-widget]').forEach(initWidget);
    }
  }
});

observer.observe(document.body, { childList: true, subtree: true });

const wrapper = document.createElement('section');
wrapper.innerHTML = '<div data-widget id="chart"></div><div data-widget id="map"></div>';
document.body.append(wrapper);
Widget initialisiert: chart Widget initialisiert: map

Auf ein bestimmtes Element warten (Promise)

const waitForElement = (selector, root = document.body) =>
  new Promise((resolve) => {
    const existing = root.querySelector(selector);
    if (existing) return resolve(existing);

    const observer = new MutationObserver((records) => {
      for (const record of records) {
        for (const node of record.addedNodes) {
          if (!(node instanceof Element)) continue;
          const match = node.matches(selector) ? node : node.querySelector(selector);
          if (match) {
            observer.disconnect();
            resolve(match);
            return;
          }
        }
      }
    });
    observer.observe(root, { childList: true, subtree: true });
  });

const run = async () => {
  setTimeout(() => {
    const btn = document.createElement('button');
    btn.className = 'cookie-accept';
    document.body.append(btn);
  }, 500);

  const button = await waitForElement('.cookie-accept');
  console.log('Gefunden:', button.className);
};
run();
Gefunden: cookie-accept

// Wichtig · Fallstricke

  • Nur direkte Kinder: Nachfahren eingefügter Knoten stehen nicht in addedNodes. Das gilt auch dann, wenn Sie mit innerHTML ganze Bäume einfügen. Die Nachfahren müssen Sie selbst mit querySelectorAll() durchsuchen.
  • Nicht nur Elemente: Die Liste kann auch Text- und Kommentarknoten enthalten, etwa Leerraum aus innerHTML. Aufrufe wie matches() scheitern auf Textknoten. Prüfen Sie deshalb vorher nodeType oder instanceof Element.
  • Verschobene Knoten: Wird ein vorhandener Knoten mit append() oder insertBefore() verschoben, erscheint er in removedNodes des alten und in addedNodes des neuen Elternelements.
  • Knoten evtl. schon wieder entfernt: Die Callbacks laufen asynchron als Mikrotask. Zu diesem Zeitpunkt kann ein Knoten aus addedNodes bereits wieder aus dem DOM entfernt sein. Prüfen Sie im Zweifel node.isConnected.
  • Voraussetzung: Die Liste wird nur befüllt, wenn der Observer mit childList: true gestartet wurde.
  • Leistung: Mit subtree: true auf document.body kann der Callback sehr oft feuern. Halten Sie die Verarbeitung schlank, und rufen Sie disconnect() auf, sobald Sie den Observer nicht mehr brauchen.
  • Kompatibilität: Wird von allen modernen Browsern unterstützt. for...of über NodeList funktioniert nicht im Internet Explorer.