Veröffentlicht am Schreib einen Kommentar

DOM-Änderungen mit MutationObserver beobachten

Immer wieder kommt es vor, dass ich auf Basis bestimmter Elemente oder Interaktionen mit Elementen per JavaScript bestimmte Aufgaben erledigen muss. Sofern das Element bereits existiert, ist das auch kein Problem, denn mit document.querySelector() und ähnlichem ist es gut möglich, das gewünschte Element anzusprechen und damit zu arbeiten.

Doch was tut man, wenn das gewünschte Element beim Laden der Seite noch gar nicht vorhanden ist? Dann bietet sich ein MutationObserver an, der Änderung am Document Object Model (DOM) mitbekommt und das Arbeiten mit diesen Veränderungen ermöglicht. Dazu zählen auch hinzugefügte Elemente.

Als Beispiel dient ein Button, der irgendwann irgendwo innerhalb des DOMs hinzugefügt wird und der beim Klick darauf ein beliebiges JavaScript ausführen soll. Das sieht dann folgendermaßen aus:

let newButton;
const newButtonObserver = new MutationObserver( function( mutations, observer ) {
	newButton = document.querySelector( '.new-button' );
	
	if ( newButton ) {
		newButton.addEventListener( 'click', function( event ) {
			// do something
		} );
		observer.disconnect();
	}
} );

// check for DOM changes
newButtonObserver.observe( document, {
	childList: true,
	subtree: true,
} );

Zeile 1: Eine Variable wird definiert, die später das Element des neu hinzugefügten Buttons beinhaltet.

Zeile 2–11: Eine Konstante wird mit dem Interface eines MutationObservers definiert. Dieser bekommt eine Fallback-Funktion mit zwei Parametern. Der erste Parameter mutations enthält bei jeder Änderung des DOMs eine Liste mit entsprechenden Veränderungen. Der zweite Parameter observer enthält das MutationObserver-Objekt.

Innerhalb der Ballback-Funktion wird dann zum einen der Button gesucht, der vorhanden sein soll. Wenn er existiert, bekommt er dann ein Klick-Event und vom MutationObserver wird die Verbindung getrennt, da in diesem Beispiel nicht mehr auf weitere Änderungen geprüft werden muss, nachdem der neue Button hinzugefügt wurde.

In Zeile 14–17 wird der MutationObserver per .observe() überhaupt erst gestartet. Als erster Parameter dient das Element, bei dem die Prüfung gestartet werden soll. In diesem Fall document, also innerhalb des gesamten DOMs. Der zweite Parameter enthält ein Objekt mit verschiedenen Attributen. In diesem Fall besagen die Attribute, dass auch Änderungen an Kindelementen beobachtet werden sollen (subtree: true) und ebenfalls das Hinzufügen/Entfernen von Elementen (childList: true). Letzteres geht nur in Verbindung mit ersterem. Eine weitere Auflistung an möglichen Attributen findet sich in der Dokumentation bei MDN.

Optimierung

Das Beispiel oben lässt sich insbesondere an zwei Stellen gut optimieren: Weiß man, in welchem Bereich der Button hinzugefügt wird, kann man das Element, bei dem die Prüfung gestartet werden soll, entsprechend einschränken, um nicht den gesamten DOM zu beobachten.

Zum anderen kann man die Liste der mutations in Zeile 3 erst einmal durchgehen und prüfen, ob überhaupt ein neues Element hinzugefügt wurde und andernfalls die weitere Ausführung unterbrechen, beispielsweise so:

for( const mutation of mutationsList ) {
	if ( mutation.type !== 'childList' ) {
		return;
	}
}

Fazit

Ich kann mich noch gut an vergangene Zeiten erinnern, in denen man mit setInterval() ein Intervall gestartet hat und in regelmäßigen Abständen so auf Änderungen geprüft hat. Dagegen ist der MutationObserver wesentlich angenehmer und auch performanter zu verwenden. Auch der Browsersupport ist gut, alles ab Internet Explorer 11 ist praktisch abgedeckt.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.