Gibt es einen JavaScript / jQuery DOM-Änderungslistener?

402

Im Wesentlichen möchte ich ein Skript ausführen lassen, wenn sich der Inhalt einer DIVÄnderung ändert. Da die Skripte separat sind (Inhaltsskript in der Chrome-Erweiterung und im Webseiten-Skript), muss ich einfach Änderungen im DOM-Status beobachten. Ich könnte eine Umfrage einrichten, aber das scheint schlampig.

Fletcher Moore
quelle

Antworten:

488

DOM3-Mutationsereignisse waren lange Zeit die beste verfügbare Lösung, wurden jedoch aus Leistungsgründen nicht mehr unterstützt. DOM4-Mutationsbeobachter sind der Ersatz für veraltete DOM3-Mutationsereignisse. Sie werden derzeit in modernen Browsern als MutationObserver(oder als Herstellerpräfix WebKitMutationObserverin alten Chrome-Versionen) implementiert :

MutationObserver = window.MutationObserver || window.WebKitMutationObserver;

var observer = new MutationObserver(function(mutations, observer) {
    // fired when a mutation occurs
    console.log(mutations, observer);
    // ...
});

// define what element should be observed by the observer
// and what types of mutations trigger the callback
observer.observe(document, {
  subtree: true,
  attributes: true
  //...
});

In diesem Beispiel werden DOM-Änderungen documentund der gesamte Teilbaum abgehört und Änderungen an Elementattributen sowie strukturelle Änderungen ausgelöst. Die Entwurfsspezifikation enthält eine vollständige Liste der gültigen Eigenschaften des Mutations-Listeners :

childList

  • Stellen Sie ein, trueob Mutationen an den Kindern des Ziels beobachtet werden sollen.

Attribute

  • Stellen Sie ein, trueob Mutationen an den Attributen des Ziels beobachtet werden sollen.

ZeichenDaten

  • Stellen Sie ein, trueob Mutationen an den Daten des Ziels beobachtet werden sollen.

Teilbaum

  • Stellen Sie ein, trueob Mutationen nicht nur zum Ziel, sondern auch zu den Nachkommen des Ziels beobachtet werden sollen.

attributeOldValue

  • Setzen Sie auf trueif attributesauf true und den Attributwert des Ziels, bevor die Mutation aufgezeichnet werden muss.

characterDataOldValue

  • Setzen Sie auf trueif characterDataauf true und die Daten des Ziels, bevor die Mutation aufgezeichnet werden muss.

attributeFilter

  • Auf eine Liste lokaler Attributnamen (ohne Namespace) setzen, wenn nicht alle Attributmutationen beachtet werden müssen.

(Diese Liste ist ab April 2014 aktuell. Sie können die Spezifikation auf Änderungen überprüfen.)

Apsiller
quelle
3
@AshrafBashir Ich sehe, dass das Beispiel in Firefox 19.0.2 einwandfrei funktioniert: Ich sehe, dass es ([{}])an der Konsole protokolliert ist, was den erwarteten Wert anzeigt, MutationRecordwenn ich darauf klicke . Bitte überprüfen Sie es erneut, da es sich möglicherweise um einen vorübergehenden technischen Fehler in JSFiddle handelt. Ich habe es noch nicht im IE getestet, da ich keinen IE 10 habe, der derzeit die einzige Version ist, die Mutationsereignisse unterstützt.
Apsiller
1
Ich habe gerade eine Antwort gepostet, die in IE10 + und fast allem anderen funktioniert.
Naugtur
1
Die Spezifikation scheint kein grünes Kästchen mehr zu haben, in dem die Optionen für den Mutationsbeobachter aufgeführt sind. Es listet die Optionen in Abschnitt 5.3.1 auf und beschreibt sie etwas weiter unten.
LS
2
@LS Danke, ich habe den Link aktualisiert, das Bit über dem grünen Kästchen entfernt und die gesamte Liste in meiner Antwort bearbeitet (nur für den Fall, dass der zukünftige Link verrottet).
Apsiller
211

Bearbeiten

Diese Antwort ist jetzt veraltet. Siehe die Antwort von Apsillers .

Da dies für eine Chrome-Erweiterung gilt, können Sie auch das Standard-DOM-Ereignis verwenden DOMSubtreeModified. Informationen zur Unterstützung dieses Ereignisses finden Sie in allen Browsern. Es wird in Chrome seit 1.0 unterstützt.

$("#someDiv").bind("DOMSubtreeModified", function() {
    alert("tree changed");
});

Sehen Sie ein funktionierendes Beispiel hier .

Anurag
quelle
Ich habe festgestellt, dass dieses Ereignis auch nach bestimmten Selektoren ausgelöst werden kann. Ich untersuche gerade.
Peder Rice
9
w3.org/TR/DOM-Level-3-Events/#event-type-DOMSubtreeModified sagt, dass dieses Ereignis veraltet ist. Was würden wir stattdessen verwenden?
Maslow
2
@ Maslow- gibt es nicht! stackoverflow.com/questions/6659662/…
Sam Rueby
4
Es gibt github.com/joelpurra/jquery-mutation-summary, das es im Grunde für jquery-Benutzer löst.
Capi Etheriel
1
Funktioniert immer noch, wenn Sie Chrome-Erweiterungen erstellen. von unschätzbarem Wert.
Konzept47
49

Viele Websites verwenden AJAX, um Inhalte dynamisch hinzuzufügen / anzuzeigen / zu ändern. Manchmal wird es anstelle der In-Site-Navigation verwendet, sodass die aktuelle URL programmgesteuert geändert wird und Inhaltsskripte in diesem Fall nicht automatisch vom Browser ausgeführt werden, da die Seite nicht vollständig vom Remote-Server abgerufen wird.


Übliche JS-Methoden zum Erkennen von Seitenänderungen, die in einem Inhaltsskript verfügbar sind .

  • MutationObserver ( docs ), um DOM-Änderungen buchstäblich zu erkennen:

  • Ereignis-Listener für Websites, die eine Änderung des Inhalts durch Senden eines DOM-Ereignisses signalisieren:

  • Regelmäßige Überprüfung des DOM über setInterval :
    Dies funktioniert natürlich nur in Fällen, in denen Sie auf das Erscheinen eines bestimmten Elements warten, das durch seine ID / Auswahl identifiziert wird, und Sie können neue dynamisch hinzugefügte Inhalte nur dann allgemein erkennen, wenn Sie eine Art Fingerabdruck erfinden die vorhandenen Inhalte.

  • Cloaking History API in einem injizierten DOM-Skript :

    document.head.appendChild(document.createElement('script')).text = '(' +
        function() {
            // injected DOM script is not a content script anymore, 
            // it can modify objects and functions of the page
            var _pushState = history.pushState;
            history.pushState = function(state, title, url) {
                _pushState.call(this, state, title, url);
                window.dispatchEvent(new CustomEvent('state-changed', {detail: state}));
            };
            // repeat the above for replaceState too
        } + ')(); this.remove();'; // remove the DOM script element
    
    // And here content script listens to our DOM script custom events
    window.addEventListener('state-changed', function(e) {
        console.log('History state changed', e.detail, location.hash);
        doSomething();
    });
  • Hören von Hashchange- und Popstate- Ereignissen:

    window.addEventListener('hashchange', function(e) {
        console.log('URL hash changed', e);
        doSomething();
    });
    window.addEventListener('popstate', function(e) {
        console.log('State changed', e);
        doSomething();
    });



Erweiterungsspezifisch: Erkennen von URL-Änderungen auf einer Hintergrund- / Ereignisseite .

Es gibt erweiterte APIs für die Navigation: webNavigation , webRequest , aber wir verwenden den einfachen Ereignis-Listener chrome.tabs.onUpdated , der eine Nachricht an das Inhaltsskript sendet :

  • manifest.json:
    declare Hintergrund / Event - Seite
    declare Inhalt Skript
    add "tabs" Erlaubnis .

  • background.js

    var rxLookfor = /^https?:\/\/(www\.)?google\.(com|\w\w(\.\w\w)?)\/.*?[?#&]q=/;
    chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
        if (rxLookfor.test(changeInfo.url)) {
            chrome.tabs.sendMessage(tabId, 'url-update');
        }
    });
  • content.js

    chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
        if (msg === 'url-update') {
            doSomething();
        }
    });
wOxxOm
quelle
29

Ein anderer Ansatz hängt davon ab, wie Sie das Div ändern. Wenn Sie JQuery verwenden, um den Inhalt eines Divs mit seiner html () -Methode zu ändern, können Sie diese Methode erweitern und jedes Mal eine Registrierungsfunktion aufrufen, wenn Sie HTML in ein Div einfügen.

(function( $, oldHtmlMethod ){
    // Override the core html method in the jQuery object.
    $.fn.html = function(){
        // Execute the original HTML method using the
        // augmented arguments collection.

        var results = oldHtmlMethod.apply( this, arguments );
        com.invisibility.elements.findAndRegisterElements(this);
        return results;

    };
})( jQuery, jQuery.fn.html );

Wir fangen nur die Aufrufe von html () ab, rufen damit eine Registrierungsfunktion auf, die sich im Kontext darauf bezieht, dass das Zielelement neuen Inhalt erhält, und leiten den Aufruf an die ursprüngliche Funktion jquery.html () weiter. Denken Sie daran, die Ergebnisse der ursprünglichen html () -Methode zurückzugeben, da JQuery dies für die Methodenverkettung erwartet.

Für weitere Informationen über Verfahren überwiegende und Erweiterung Besuche http://www.bennadel.com/blog/2009-Using-Self-Executing-Function-Arguments-To-Override-Core-jQuery-Methods.htm , das ist , wo Ich habe die Verschlussfunktion eingestellt. Schauen Sie sich auch das Plugins-Tutorial auf der JQuery-Website an.

Zac Imboden
quelle
7

Zusätzlich zu den von der MutationObserverAPI bereitgestellten "Raw" -Tools gibt es "Convenience" -Bibliotheken für die Arbeit mit DOM-Mutationen.

Bedenken Sie: MutationObserver repräsentiert jede DOM-Änderung in Form von Teilbäumen. Wenn Sie beispielsweise darauf warten, dass ein bestimmtes Element eingefügt wird, befindet es sich möglicherweise tief in den untergeordneten Elementen von mutations.mutation[i].addedNodes[j].

Ein weiteres Problem besteht darin, dass Ihr eigener Code als Reaktion auf Mutationen das DOM ändert - Sie möchten es häufig herausfiltern.

Eine gute Komfortbibliothek, die solche Probleme löst, ist mutation-summary(Haftungsausschluss: Ich bin nicht der Autor, nur ein zufriedener Benutzer), mit der Sie Abfragen zu Ihren Interessen angeben und genau das erhalten können.

Grundlegendes Anwendungsbeispiel aus den Dokumenten:

var observer = new MutationSummary({
  callback: updateWidgets,
  queries: [{
    element: '[data-widget]'
  }]
});

function updateWidgets(summaries) {
  var widgetSummary = summaries[0];
  widgetSummary.added.forEach(buildNewWidget);
  widgetSummary.removed.forEach(cleanupExistingWidget);
}
Xan
quelle