Czy istnieje moduł nasłuchujący zmiany DOM JavaScript / jQuery?

402

Zasadniczo chcę, aby skrypt był wykonywany, gdy treść DIVzmiany. Ponieważ skrypty są osobne (skrypt treści w rozszerzeniu Chrome i skrypcie strony internetowej), potrzebuję sposobu, aby po prostu obserwować zmiany w stanie DOM. Mógłbym skonfigurować odpytywanie, ale to wydaje się niechlujne.

Fletcher Moore
źródło

Odpowiedzi:

488

Przez długi czas zdarzenia mutacji DOM3 były najlepszym dostępnym rozwiązaniem, ale były przestarzałe ze względu na wydajność. Obserwatorzy mutacji DOM4 zastępują przestarzałe zdarzenia mutacji DOM3. Są one obecnie zaimplementowane w nowoczesnych przeglądarkach jako MutationObserver(lub jako prefiks dostawcy WebKitMutationObserverw starych wersjach Chrome):

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
  //...
});

Ten przykład nasłuchuje zmian DOM w documentcałym poddrzewie i będzie odpalał zmiany atrybutów elementu, a także zmiany strukturalne. Wersja robocza ma pełną listę poprawnych właściwości detektora mutacji :

childList

  • Ustaw, trueczy mutacje u dzieci docelowych mają być obserwowane.

atrybuty

  • Ustaw, trueczy mutacje atrybutów celu mają być obserwowane.

characterData

  • Ustaw, trueczy mutacje danych celu mają być obserwowane.

poddrzewo

  • Ustaw, trueczy mutacje mają nie tylko celować, ale także potomków celu mają być obserwowane.

attributeOldValue

  • Ustawiony na trueif attributesjest ustawiony na wartość true, a wartość atrybutu celu przed zarejestrowaniem mutacji.

characterDataOldValue

  • Ustawiony na trueif characterDatajest ustawiony na wartość true i dane celu przed zarejestrowaniem mutacji.

attributeFilter

  • Ustaw na listę lokalnych nazw atrybutów (bez przestrzeni nazw), jeśli nie wszystkie mutacje atrybutów muszą być przestrzegane.

(Ta lista jest aktualna od kwietnia 2014 r .; możesz sprawdzić specyfikację pod kątem zmian).

apsillery
źródło
3
@AshrafBashir Widzę próbkę działającą poprawnie w Firefoksie 19.0.2: Widzę ([{}])zalogowany do konsoli, która pokazuje oczekiwane MutationRecordpo kliknięciu. Sprawdź ponownie, ponieważ mogła to być tymczasowa awaria techniczna JSFiddle. Nie testowałem go jeszcze w IE, ponieważ nie mam IE 10, który jest obecnie jedyną wersją obsługującą zdarzenia mutacji.
apsillers
1
Właśnie opublikowałem odpowiedź, która działa w IE10 + i przeważnie na cokolwiek innego.
naugtur
1
Wygląda na to, że specyfikacja nie ma już zielonego pola z listą opcji obserwatora mutacji. Wymienia opcje w sekcji 5.3.1 i opisuje je nieco poniżej.
LS
2
@LS Dzięki, zaktualizowałem link, usunąłem trochę o zielonym polu i zredagowałem całą listę do mojej odpowiedzi (na wszelki wypadek zepsucia linku w przyszłości).
apsillers
3
Oto tabela zgodności przeglądarki od Can I Use.
bdesham
211

Edytować

Ta odpowiedź jest już nieaktualna. Zobacz odpowiedź apsillerów .

Ponieważ dotyczy to rozszerzenia Chrome, równie dobrze możesz użyć standardowego zdarzenia DOM - DOMSubtreeModified. Zobacz obsługę tego wydarzenia w różnych przeglądarkach. Jest obsługiwany w Chrome od wersji 1.0.

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

Zobacz działający przykład tutaj .

Anurag
źródło
Zauważyłem, że to wydarzenie można odpalić nawet po niektórych selektorach. Obecnie badam.
Peder Rice,
9
w3.org/TR/DOM-Level-3-Events/#event-type-DOMSubtreeModified mówi, że to zdarzenie jest przestarzałe, czego byśmy użyli zamiast tego?
Masłów
2
@ Maslow- Nie ma! stackoverflow.com/questions/6659662/…
Sam Rueby
4
Istnieje github.com/joelpurra/jquery-mutation-summary, który w zasadzie rozwiązuje go dla użytkowników jquery.
Capi Etheriel
1
Nadal działa, jeśli budujesz rozszerzenia chromowane. nieoceniony.
concept47
49

Wiele witryn używa AJAX do dynamicznego dodawania / pokazywania / zmiany treści. Czasami jest używany zamiast nawigacji w witrynie, więc bieżący adres URL jest programowo zmieniany, a skrypty treści nie są automatycznie wykonywane przez przeglądarkę, ponieważ strona nie jest w całości pobierana ze zdalnego serwera.


Dostępne w JS metody wykrywania zmian strony dostępne w skrypcie treści .

  • MutationObserver ( dokumenty ), aby dosłownie wykryć zmiany DOM:

  • Detektor zdarzeń dla witryn sygnalizujących zmianę treści przez wysłanie zdarzenia DOM:

  • Okresowe sprawdzanie DOM za pomocą setInterval :
    Oczywiście będzie to działać tylko wtedy, gdy będziesz czekać na pojawienie się określonego elementu identyfikowanego przez jego identyfikator / selektor, i nie pozwoli ci na powszechne wykrywanie nowej dynamicznie dodanej zawartości, chyba że wymyślisz jakiś odcisk palca istniejąca zawartość.

  • Interfejs API historii maskowania wewnątrz wstrzykiwanego skryptu DOM :

    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();
    });
  • Słuchanie hashchange , wydarzeń popstate :

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



Specyficzne dla rozszerzeń: wykrywanie zmian adresów URL na stronie w tle / zdarzeniu .

Istnieją zaawansowane API do pracy z nawigacją: webNavigation , webRequest , ale użyjemy prostego detektora zdarzeń chrome.tabs.onUpdated , który wysyła komunikat do skryptu treści:

  • manifest.json:
    deklaruj stronę w tle / zdarzenie
    deklaruj uprawnienia do skryptu
    dodawania ."tabs"

  • 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
źródło
29

Inne podejście w zależności od tego, jak zmieniasz div. Jeśli używasz JQuery do zmiany zawartości div za pomocą metody html (), możesz rozszerzyć tę metodę i wywołać funkcję rejestracji za każdym razem, gdy wstawisz html do div.

(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 );

Po prostu przechwytujemy wywołania html (), wywołujemy funkcję rejestracji, która w kontekście odnosi się do elementu docelowego, który otrzymuje nową treść, a następnie przekazujemy wywołanie oryginalnej funkcji jquery.html (). Pamiętaj o zwróceniu wyników oryginalnej metody html (), ponieważ JQuery spodziewa się tego w przypadku łączenia metod.

Aby uzyskać więcej informacji o zastępowaniu i rozszerzaniu metod, sprawdź http://www.bennadel.com/blog/2009-Using-Self-Executing-Function-Arguments-To-Override-Core-jQuery-Methods.htm , gdzie Przekopałem funkcję zamknięcia. Sprawdź także samouczek wtyczek na stronie JQuery.

Zac Imboden
źródło
7

Oprócz „surowych” narzędzi udostępnianych przez MutationObserverAPI , istnieją biblioteki „wygody” do pracy z mutacjami DOM.

Zastanów się: MutationObserver reprezentuje każdą zmianę DOM pod względem poddrzewa. Jeśli więc na przykład czekasz na wstawienie określonego elementu, może on znajdować się głęboko w elementach potomnych mutations.mutation[i].addedNodes[j].

Innym problemem jest sytuacja, gdy własny kod w reakcji na mutacje zmienia DOM - często chcesz go odfiltrować.

Dobrą wygodną biblioteką, która rozwiązuje takie problemy, jest mutation-summary(zrzeczenie się odpowiedzialności: nie jestem autorem, tylko zadowolonym użytkownikiem), która pozwala określić zapytania dotyczące tego, co Cię interesuje, i uzyskać dokładnie to.

Podstawowy przykład użycia z dokumentów:

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
źródło