Start · Sprachen · JavaScript · Referenz · MutationRecord.previousSibling

MutationRecord.previousSibling

Eigenschaft

Schreibgeschützt: der Geschwisterknoten direkt vor den hinzugefügten oder entfernten Kindknoten einer <code>childList</code>-Mutation oder <code>null</code>.

seit JavaScript Baseline: alle Browser (DOM4, seit ca. 2012) Kategorie: webapi

Signatur

MutationRecord.previousSibling

Beschreibung

Die schreibgeschützte Eigenschaft previousSibling eines MutationRecord gibt den Knoten zurück, der im target unmittelbar vor den Knoten stand, die in addedNodes oder removedNodes aufgeführt sind. So lässt sich genau feststellen, an welcher Stelle Knoten eingefügt oder entfernt wurden.

Einen Wert hat die Eigenschaft nur bei Datensätzen vom Typ "childList". Bei "attributes"- und "characterData"-Mutationen ist sie immer null. Auch bei childList-Mutationen ist sie null, wenn die geänderten Knoten am Anfang der Kindliste standen bzw. eingefügt wurden, es also keinen Vorgänger gibt.

Zusammen mit nextSibling beschreibt previousSibling die Position der Änderung eindeutig. Das hilft, wenn man DOM-Änderungen nachvollziehen, protokollieren oder in einer anderen Struktur spiegeln will, etwa beim Synchronisieren eines virtuellen DOMs, bei Undo/Redo-Funktionen oder bei Werkzeugen zur Aufzeichnung von Sitzungen.

Der Wert zeigt den Stand zum Zeitpunkt der Mutation. Wurde das DOM danach weiter verändert, kann der Knoten inzwischen woanders stehen oder schon entfernt sein.

Rückgabewert

Typ
Node | null
Beschreibung
Der Geschwisterknoten vor den hinzugefügten oder entfernten Knoten. Das kann auch ein Text- oder Kommentarknoten sein. null, wenn es keinen Vorgänger gibt oder der Datensatz nicht vom Typ childList ist.

Beispiele

Einfügeposition eines neuen Listeneintrags ermitteln

const list = document.createElement('ul');
list.innerHTML = '<li id="a">A</li><li id="b">B</li>';
document.body.append(list);

const observer = new MutationObserver((records) => {
  for (const record of records) {
    if (record.type !== 'childList') continue;
    const prev = record.previousSibling;
    console.log('Vorgänger:', prev ? prev.id : null);
    console.log('Neu:', [...record.addedNodes].map((n) => n.id));
  }
});

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

// Neues Element hinter #a einfügen
const c = document.createElement('li');
c.id = 'c';
c.textContent = 'C';
list.insertBefore(c, list.querySelector('#b'));
Vorgänger: a Neu: ["c"]

null beim Einfügen am Anfang

const box = document.createElement('div');
box.append(document.createElement('span'));
document.body.append(box);

new MutationObserver(([record]) => {
  console.log(record.previousSibling); // kein Vorgänger
  console.log(record.nextSibling.nodeName);
}).observe(box, { childList: true });

box.prepend(document.createElement('strong'));
null SPAN

Entfernte Knoten samt Position protokollieren (z. B. für Undo)

const editor = document.querySelector('#editor') ?? document.body;
const undoStack = [];

const observer = new MutationObserver((records) => {
  for (const { type, target, removedNodes, previousSibling, nextSibling } of records) {
    if (type !== 'childList' || removedNodes.length === 0) continue;
    undoStack.push({ parent: target, nodes: [...removedNodes], previousSibling, nextSibling });
  }
});
observer.observe(editor, { childList: true, subtree: true });

// Letzte Entfernung rückgängig machen
const undo = () => {
  const entry = undoStack.pop();
  if (!entry) return;
  observer.disconnect(); // eigene Änderung nicht erneut aufzeichnen
  const ref = entry.nextSibling ?? (entry.previousSibling ? entry.previousSibling.nextSibling : entry.parent.firstChild);
  for (const node of entry.nodes) entry.parent.insertBefore(node, ref);
  observer.observe(editor, { childList: true, subtree: true });
};

// Wichtig · Fallstricke

  • Text- und Kommentarknoten: previousSibling ist ein beliebiger Node, kein Element. Leerzeichen und Zeilenumbrüche im HTML ergeben oft Textknoten als Vorgänger. Prüfe darum nodeType oder suche selbst nach dem nächsten Element (z. B. mit previousElementSibling am aktuellen Knoten).
  • Nur bei childList: Bei Attribut- und Textänderungen ist der Wert immer null. Deshalb zuerst record.type prüfen.
  • Zusammengefasste Datensätze: Bei Operationen wie innerHTML = … oder replaceChildren() entsteht oft nur ein Datensatz mit vielen Knoten. previousSibling gilt dann für den ganzen Block, nicht für jeden einzelnen Knoten.
  • Momentaufnahme: Da Callbacks asynchron als Mikrotask laufen, kann sich das DOM seit der Mutation bereits geändert haben. Der Vorgänger ist dann womöglich nicht mehr im Dokument.
  • In allen modernen Browsern unterstützt. Vorgänger waren die veralteten Mutation Events (z. B. DOMNodeInserted), die nicht mehr verwendet werden sollten.