Start · Sprachen · JavaScript · Referenz · MutationRecord.removedNodes

MutationRecord.removedNodes

Eigenschaft

Schreibgeschützte <code>NodeList</code> mit den Knoten, die bei einer von einem <code>MutationObserver</code> beobachteten Mutation aus dem Zielknoten entfernt wurden.

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

Signatur

MutationRecord.removedNodes

Beschreibung

Die Eigenschaft removedNodes eines MutationRecord enthält alle Kindknoten, die bei einer einzelnen DOM-Änderung aus dem Zielknoten (record.target) entfernt wurden. Befüllt wird sie nur bei Datensätzen vom Typ "childList". Den Observer muss man dafür mit der Option childList: true starten, optional mit subtree: true, wenn auch Änderungen in tieferen Ebenen erfasst werden sollen.

Typische Einsatzfälle sind Aufräumarbeiten, wenn Elemente aus dem DOM verschwinden. Dazu gehört zum Beispiel, Event-Listener, Timer oder Instanzen von Drittbibliotheken freizugeben, ein Widget zu zerstören, das Entfernen eines Elements zu protokollieren oder einen Zustand zu synchronisieren, etwa einen Zähler für Listeneinträge.

Die NodeList ist statisch. Sie ändert sich also nicht, wenn das DOM danach weiter verändert wird. Sie kann jede Art von Knoten enthalten, also auch Textknoten (Node.TEXT_NODE) und Kommentare. Wurde nichts entfernt, etwa bei "attributes"- oder "characterData"-Datensätzen, ist die Liste leer, aber nie null. Zusammen mit previousSibling und nextSibling lässt sich genau bestimmen, an welcher Stelle die Knoten entfernt wurden.

Rückgabewert

Typ
NodeList
Beschreibung
Eine statische NodeList der entfernten Knoten. Sie ist leer (length === 0), wenn bei dieser Mutation keine Knoten entfernt wurden.

Beispiele

Entfernte Listeneinträge protokollieren

const liste = document.querySelector('#todo-liste');

const observer = new MutationObserver((records) => {
  for (const record of records) {
    if (record.type !== 'childList') continue;
    for (const node of record.removedNodes) {
      // Nur Elemente berücksichtigen, keine Text- oder Kommentarknoten
      if (node.nodeType === Node.ELEMENT_NODE) {
        console.log('Entfernt:', node.textContent.trim());
      }
    }
  }
});

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

// Mutation auslösen
liste.firstElementChild?.remove();
Entfernt: Einkaufen gehen

Aufräumen, wenn ein Widget aus dem DOM verschwindet

const widgets = new WeakMap();

const initWidget = (el) => {
  const id = setInterval(() => { el.textContent = new Date().toLocaleTimeString(); }, 1000);
  widgets.set(el, () => clearInterval(id));
};

document.querySelectorAll('.uhr').forEach(initWidget);

const observer = new MutationObserver((records) => {
  for (const { removedNodes } of records) {
    removedNodes.forEach((node) => {
      if (!(node instanceof Element)) return;
      // Auch verschachtelte Widgets im entfernten Teilbaum finden
      const betroffen = [node, ...node.querySelectorAll('.uhr')];
      for (const el of betroffen) {
        const cleanup = widgets.get(el);
        if (cleanup && !el.isConnected) {
          cleanup();
          widgets.delete(el);
          console.log('Timer gestoppt für', el);
        }
      }
    });
  }
});

observer.observe(document.body, { childList: true, subtree: true });
Timer gestoppt für <div class="uhr">…</div>

Hinzugefügte und entfernte Knoten zählen

const container = document.createElement('div');
container.innerHTML = '<p>A</p><p>B</p>';
document.body.append(container);

const observer = new MutationObserver((records) => {
  const entfernt = records.reduce((sum, r) => sum + r.removedNodes.length, 0);
  const hinzugefuegt = records.reduce((sum, r) => sum + r.addedNodes.length, 0);
  console.log({ entfernt, hinzugefuegt });
});

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

// innerHTML ersetzt alle Kinder: 2 entfernt, 1 hinzugefügt
container.innerHTML = '<p>C</p>';
{ entfernt: 2, hinzugefuegt: 1 }

// Wichtig · Fallstricke

  • Nur bei childList: Ohne childList: true in observe() bleibt removedNodes immer leer. Bei Attribut- oder Textänderungen ist die Liste ebenfalls leer.
  • Verschieben zählt als Entfernen: appendChild() oder insertBefore() mit einem Knoten, der schon im DOM hängt, erzeugt einen Datensatz mit removedNodes für die alte Position. Deshalb vor dem Aufräumen node.isConnected prüfen, damit verschobene Elemente nicht versehentlich zerstört werden.
  • Nur direkte Kinder: In der Liste stehen nur die Wurzeln der entfernten Teilbäume. Nachfahren darin muss man selbst suchen, zum Beispiel mit querySelectorAll(). Auch mit subtree: true erzeugt das Entfernen eines Elternknotens keine eigenen Datensätze für seine Kinder.
  • Textknoten: Durch Leerraum im HTML landen oft Textknoten in der Liste. Deshalb nach nodeType oder instanceof Element filtern.
  • Asynchron und gebündelt: Der Callback läuft als Microtask und erhält mehrere Datensätze auf einmal. Rechenintensive Arbeit pro Knoten kann bei großen DOM-Änderungen die Leistung bremsen.
  • Die Eigenschaft ist schreibgeschützt und wird von allen modernen Browsern unterstützt. Das veraltete DOMNodeRemoved-Mutation-Event ist abgekündigt und sollte nicht mehr verwendet werden.