Signatur
Beschreibung
MutationRecord ist ein schreibgeschütztes Objekt, das der Browser selbst erzeugt. Man instanziiert es nie direkt: Ein MutationObserver sammelt DOM-Änderungen und übergibt seinem Callback ein Array solcher Einträge. Außerdem liefert MutationObserver.prototype.takeRecords() die noch nicht verarbeiteten Einträge.
Jeder Eintrag steht für genau eine Änderung. Welche Eigenschaften befüllt sind, hängt von type ab:
type–"attributes","characterData"oder"childList".target– der betroffene Knoten: bei Attributen das Element, beicharacterDatader Text-/Kommentarknoten, beichildListder Elternknoten.addedNodes/removedNodes– eineNodeListder eingefügten bzw. entfernten Knoten (leer, wenn der Typ nichtchildListist).previousSibling/nextSibling– Geschwisterknoten der eingefügten bzw. entfernten Knoten odernull.attributeName/attributeNamespace– Name und Namensraum des geänderten Attributs (sonstnull).oldValue– vorheriger Wert, aber nur, wenn beim BeobachtenattributeOldValuebzw.characterDataOldValueauftruegesetzt wurde; sonstnull.
Typische Einsatzfälle: dynamisch nachgeladene Elemente erkennen (z. B. Widgets initialisieren), Attributänderungen verfolgen (Klassen, aria-*), Undo-Funktionen für contenteditable oder Debugging von Code anderer Hersteller, der das DOM verändert.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $type Pflicht | "attributes" | "characterData" | "childList" | Schreibgeschützt. Art der Änderung. | |
| $target Pflicht | Node | Schreibgeschützt. Knoten, an dem die Änderung stattfand (bei childList der Elternknoten). |
|
| $addedNodes Pflicht | NodeList | leere NodeList | Schreibgeschützt. Eingefügte Knoten (nur bei childList). |
| $removedNodes Pflicht | NodeList | leere NodeList | Schreibgeschützt. Entfernte Knoten (nur bei childList). |
| $previousSibling | Node | null | null | Schreibgeschützt. Vorheriges Geschwister der eingefügten/entfernten Knoten. |
| $nextSibling | Node | null | null | Schreibgeschützt. Nächstes Geschwister der eingefügten/entfernten Knoten. |
| $attributeName | string | null | null | Schreibgeschützt. Lokaler Name des geänderten Attributs. |
| $attributeNamespace | string | null | null | Schreibgeschützt. Namensraum des geänderten Attributs (z. B. bei SVG/XLink). |
| $oldValue | string | null | null | Schreibgeschützt. Alter Wert des Attributs bzw. Textes – nur mit attributeOldValue/characterDataOldValue: true. |
Rückgabewert
MutationRecord[] an den MutationObserver-Callback bzw. von takeRecords() geliefert.Beispiele
Einträge im Callback nach Typ auswerten
const liste = document.createElement('ul');
document.body.append(liste);
const observer = new MutationObserver((records) => {
for (const record of records) {
switch (record.type) {
case 'childList':
console.log('Hinzugefügt:', record.addedNodes.length, 'Entfernt:', record.removedNodes.length);
break;
case 'attributes':
console.log(`Attribut ${record.attributeName}: "${record.oldValue}" → "${record.target.getAttribute(record.attributeName)}"`);
break;
}
}
});
observer.observe(liste, { childList: true, attributes: true, attributeOldValue: true });
liste.append(document.createElement('li'));
liste.className = 'aktiv';
Nachgeladene Elemente automatisch initialisieren
const initTooltip = (el) => {
el.title = el.dataset.tooltip;
console.log('Tooltip initialisiert:', el.dataset.tooltip);
};
const observer = new MutationObserver((records) => {
for (const { addedNodes } of records) {
for (const node of addedNodes) {
// Nur Elemente prüfen, Textknoten ignorieren
if (node.nodeType !== Node.ELEMENT_NODE) continue;
if (node.matches('[data-tooltip]')) initTooltip(node);
node.querySelectorAll('[data-tooltip]').forEach(initTooltip);
}
}
});
observer.observe(document.body, { childList: true, subtree: true });
const btn = document.createElement('button');
btn.dataset.tooltip = 'Speichern';
document.body.append(btn);
Textänderungen mit altem Wert und takeRecords()
const absatz = document.createElement('p');
absatz.textContent = 'Entwurf';
document.body.append(absatz);
const observer = new MutationObserver(() => {});
observer.observe(absatz, { characterData: true, subtree: true, characterDataOldValue: true });
absatz.firstChild.data = 'Final';
// Ausstehende Einträge synchron abholen, bevor der Callback läuft
const [record] = observer.takeRecords();
console.log(record.type, record.oldValue, '→', record.target.data);
observer.disconnect();
// Wichtig · Fallstricke
- Asynchron und gebündelt: Die Einträge kommen als Mikrotask nach der Änderung an, mehrere Änderungen landen in einem Array.
targetspiegelt den aktuellen Zustand wider, nicht den Zustand zum Zeitpunkt der Änderung. Bei mehreren Änderungen hintereinander kann der aktuelle Wert also schon „weiter“ sein als der Eintrag. oldValueist oftnull: Es wird nur befüllt, wennattributeOldValuebzw.characterDataOldValuegesetzt ist. BeichildListist es immernull.addedNodesenthält auch Textknoten und nur die direkt eingefügten Wurzelknoten. Deren Nachfahren muss man selbst mitquerySelectorAlldurchsuchen. Ein Knoten kann in einem Stapel zugleich inaddedNodesundremovedNodesauftauchen (z. B. bei Verschiebungen). Prüfe deshalbnode.isConnected.innerHTML/textContentam Element erzeugenchildList-Einträge und keincharacterData. Letzteres entsteht nur, wenn man einen bestehenden Textknoten direkt ändert (data,nodeValue).- Performance: Mit
subtree: trueaufdocument.bodykönnen sehr viele Einträge entstehen. Halte den Callback schlank, filtere früh und rufedisconnect()auf, wenn du nicht mehr beobachten musst. Ändert der Callback selbst das beobachtete DOM, kann eine Endlosschleife entstehen. - Die Eigenschaften sind schreibgeschützt. Es gibt keinen öffentlichen Konstruktor,
new MutationRecord()wirft einenTypeError. - Wird von allen aktuellen Browsern unterstützt. Das veraltete Mutation-Events-API (
DOMNodeInsertedu. a.) ist deprecated und wurde teilweise entfernt. Verwende stattdessenMutationObserver.