Zdarzenie jQuery: wykrywa zmiany w html / text div

265

Mam div, który ma jej zawartość zmienia się cały czas, czy to ajax requests, jquery functions, bluretc etc.

Czy jest jakiś sposób na wykrycie jakichkolwiek zmian na moim divie w dowolnym momencie?

Nie chcę używać żadnych interwałów ani sprawdzonych wartości domyślnych.

Zrobiłoby to coś takiego

$('mydiv').contentchanged() {
 alert('changed')
}
BoqBoq
źródło
5
Możliwy duplikat: stackoverflow.com/questions/10328102/...
Rob
14
@Rob To powiązanie keypresszdarzenia z <div>elementem zadowalającym treść . Nie jestem pewien, czy rozwiązania w tym zakresie mają zastosowanie. Na pewno nie zauważyliby żadnych zmian programowych w treści elementu.
Anthony Grist

Odpowiedzi:

407

Jeśli nie chcesz używać timera i sprawdzać innerHTML, możesz spróbować tego wydarzenia

$('mydiv').bind('DOMSubtreeModified', function(){
  console.log('changed');
});

Więcej informacji i dane dotyczące wsparcia przeglądarki znajdują się tutaj .

Uwaga: w nowszych wersjach jQuery funkcja bind () jest przestarzała, dlatego zamiast niej należy użyć funkcji on ():

$('body').on('DOMSubtreeModified', 'mydiv', function(){
  console.log('changed');
});
iman
źródło
14
Należy pamiętać, że DOMSubtreeModified nie jest obsługiwany w IE8 (i poniżej).
Gavin
56
to wydarzenie jest nieaktualne w3.org/TR/DOM-Level-3-Events/#glossary-deprecated
Isaiyavan Babu Karan
3
Mozilla 33: wpadła w rekurencję dla elementu <body>. Potrzebowałem znaleźć inny sposób
Chaki_Black
17
To poważne. NIE używaj tego wydarzenia, bo to zawiesi całą twoją pracę, ponieważ cały czas jest zwolniony. Zamiast tego użyj zdarzeń poniżej $ ('. MyDiv'). Bind ('DOMNodeInserted DOMNodeRemoved', function () {});
George SEDRA,
19
Ta metoda jest przestarzała! Zamiast tego użyj:$("body").on('DOMSubtreeModified', "mydiv", function() { });
Asif
55

Korzystanie z Javascript MutationObserver

  //More Details https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
 // select the target node
var target = document.querySelector('mydiv')
// create an observer instance
var observer = new MutationObserver(function(mutations) {
  console.log($('mydiv').text());   
});
// configuration of the observer:
var config = { attributes: true, childList: true, characterData: true };
// pass in the target node, as well as the observer options
observer.observe(target, config);
PPB
źródło
6
To jest poprawna odpowiedź, ponieważ jest to obecnie preferowane przy użyciu DOMSubtreeModified
Joel Davey
3
Dostaję błąd, mimo że podałem prawidłowy selektor. „VM21504: 819 Uncaught TypeError: Nie można wykonać polecenia„ observ ”na„ MutationObserver ”: parametr 1 nie jest typu„ Node ”.”
samir
42

Możesz tego spróbować

$('.myDiv').bind('DOMNodeInserted DOMNodeRemoved', function() {

});

ale to może nie działać w Internet Explorerze, nie przetestowałem tego

użytkownik1790464
źródło
3
UWAGA !: jeśli użyjesz tego jako wyzwalacza i na stronie wprowadzonych zostanie wiele zmian - uruchomi ona twoją funkcję X razy (może nawet X = 1000 lub więcej), co może być bardzo nieefektywne. Jednym prostym rozwiązaniem jest zdefiniowanie „działającego” warunku logicznego, który będzie… if (running == true) {return} ... bez uruchamiania kodu, jeśli jest już uruchomiony. Ustaw running = true zaraz po logice if, a running = false przed zamknięciem funkcji. Możesz także użyć timera, aby ograniczyć swoją funkcję do uruchamiania tylko co X sekund. bieganie = prawda; setTimeout (function () {running = false}, 5000); (lub coś lepszego)
JxAxMxIxN
Użyłem tego w polu wyboru, do którego dodano i usunięto opcje. Działało świetnie, gdy dodawano przedmioty, ale wydawało się, że było 1 element za nimi. Gdy ostatnia opcja została usunięta, nie strzelała.
CodeMonkey,
2
@JxAxMxIxN Możesz również zwiększyć limit czasu, usuwając i ponownie ustawiając limit czasu:clearTimeout(window.something); window.something = setTimeout(...);
Ctrl-C
zgodził się - twoja droga jest najlepsza - odkąd uczyłem się Pythona, wyjaśniłem wiele moich złych praktyk kodowania w wielu językach (nie wszystkie, tylko bardzo;)
JxAxMxIxN
33

Szukasz MutationObserver lub Mutation Events . Ani nie są obsługiwane wszędzie, ani nie są zbyt czule postrzegane przez świat programistów.

Jeśli wiesz (i możesz się upewnić, że) rozmiar div zmieni się, być może będziesz mógł użyć zdarzenia zmiany rozmiaru crossbrowser .

rodneyrehm
źródło
1
To jest jeden. W szczególności DOMSubtreeModified . Pomocna może być biblioteka podsumowań mutacji i ta lista zdarzeń drzewa DOM .
BenjaminRH
1
To wydarzenie jest przestarzała developer.mozilla.org/en-US/docs/Web/Guide/Events/...
Adrien Be
9
Jeśli ktokolwiek musiałby spróbować przeczytać wszystko wszędzie, to jest poprawna odpowiedź. Zdarzenia mutacji były obsługiwane w poprzednich przeglądarkach, Mutation Observer jest tym, co będzie obsługiwane w nowoczesnych przeglądarkach i będzie obsługiwane w przyszłości. Zobacz link do wsparcia: CANIUSE Mutation Observer
Josh Mc
20

Poniższy kod działa dla mnie.

$("body").on('DOMSubtreeModified', "mydiv", function() {
    alert('changed');
});

Mam nadzieję, że to komuś pomoże :)

Sanchit Gupta
źródło
To jest ta sama odpowiedź, jak ta z @Artley
Black
@Black Thankyou! Właśnie sprawdziłem odpowiedź Artleya. Zajmę się tym następnym razem.
Sanchit Gupta
17

Nie ma wbudowanego rozwiązania tego problemu, jest to problem z twoim projektem i wzorcem kodowania.

Możesz użyć wzorca wydawcy / subskrybenta. W tym celu możesz użyć niestandardowych zdarzeń jQuery lub własnego mechanizmu zdarzeń.

Pierwszy,

function changeHtml(selector, html) {
    var elem = $(selector);
    jQuery.event.trigger('htmlchanging', { elements: elem, content: { current: elem.html(), pending: html} });
    elem.html(html);
    jQuery.event.trigger('htmlchanged', { elements: elem, content: html });
}

Teraz możesz subskrybować wydarzenia divhtmlzmienianie / divhtmlzmieniane wydarzenia w następujący sposób,

$(document).bind('htmlchanging', function (e, data) {
    //your before changing html, logic goes here
});

$(document).bind('htmlchanged', function (e, data) {
    //your after changed html, logic goes here
});

Teraz musisz zmienić swoje zmiany treści div za pomocą tej changeHtml()funkcji. Można więc odpowiednio monitorować lub wprowadzać niezbędne zmiany, ponieważ argument danych powiązania zwrotnego zawierający informacje.

Musisz zmienić HTML swojego diva w ten sposób;

changeHtml('#mydiv', '<p>test content</p>');

Możesz także użyć tego dla dowolnego elementu (elementów) HTML oprócz elementu wejściowego. W każdym razie możesz to zmodyfikować, aby używać z dowolnym elementem (elementami).

Sameera Millavithanachchi
źródło
Aby obserwować zmiany w danym elemencie i działać na niego, wystarczy zmodyfikować funkcję changeHtml, aby użyć „elem.trigger (...)” zamiast „jQuery.event.trigger (...)”, a następnie powiązać z elementem, takim jak $ ('# my_element_id'). on ('htmlchanged', function (e, data) {...}
KenB
8
„jest to problem z twoim projektem i wzorcem kodowania”, co zrobić, jeśli dołączasz skrypty innych firm, a zatem nie masz kontroli nad ich kodem źródłowym? ale musisz wykryć ich zmiany w jednym divie?
DrLightman,
@DrLightman podstawową zasadą jest wybranie lib osoby trzeciej ze zdarzeniem oddzwonienia
Marcel Djaman
8

Użyj MutationObserver jak widać w tym fragmencie dostarczonym przez Mozillę i zaadaptowanym z tego postu na blogu

Alternatywnie możesz użyć przykładu JQuery widocznego w tym łączu

Chrome 18+, Firefox 14+, IE 11+, Safari 6+

// Select the node that will be observed for mutations
var targetNode = document.getElementById('some-id');

// Options for the observer (which mutations to observe)
var config = { attributes: true, childList: true };

// Callback function to execute when mutations are observed
var callback = function(mutationsList) {
    for(var mutation of mutationsList) {
        if (mutation.type == 'childList') {
            console.log('A child node has been added or removed.');
        }
        else if (mutation.type == 'attributes') {
            console.log('The ' + mutation.attributeName + ' attribute was modified.');
        }
    }
};

// Create an observer instance linked to the callback function
var observer = new MutationObserver(callback);

// Start observing the target node for configured mutations
observer.observe(targetNode, config);

// Later, you can stop observing
observer.disconnect();
Anthony Awuley
źródło
3

Możesz przechowywać stary innerHTML div w zmiennej. Ustaw interwał, aby sprawdzić, czy stara treść pasuje do bieżącej. Kiedy to nie jest prawda, zrób coś.

codelove
źródło
1

Wypróbuj MutationObserver:

obsługa przeglądarki: http://caniuse.com/#feat=mutationobserver

<html>
  <!-- example from Microsoft https://developer.microsoft.com/en-us/microsoft-edge/platform/documentation/dev-guide/dom/mutation-observers/ -->

  <head>
    </head>
  <body>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script type="text/javascript">
      // Inspect the array of MutationRecord objects to identify the nature of the change
function mutationObjectCallback(mutationRecordsList) {
  console.log("mutationObjectCallback invoked.");

  mutationRecordsList.forEach(function(mutationRecord) {
    console.log("Type of mutation: " + mutationRecord.type);
    if ("attributes" === mutationRecord.type) {
      console.log("Old attribute value: " + mutationRecord.oldValue);
    }
  });
}
      
// Create an observer object and assign a callback function
var observerObject = new MutationObserver(mutationObjectCallback);

      // the target to watch, this could be #yourUniqueDiv 
      // we use the body to watch for changes
var targetObject = document.body; 
      
// Register the target node to observe and specify which DOM changes to watch
      
      
observerObject.observe(targetObject, { 
  attributes: true,
  attributeFilter: ["id", "dir"],
  attributeOldValue: true,
  childList: true
});

// This will invoke the mutationObjectCallback function (but only after all script in this
// scope has run). For now, it simply queues a MutationRecord object with the change information
targetObject.appendChild(document.createElement('div'));

// Now a second MutationRecord object will be added, this time for an attribute change
targetObject.dir = 'rtl';


      </script>
    </body>
  </html>

juFo
źródło
0

Dodanie części treści do div, czy to przez jQuery, czy bezpośrednio przez DOM-API, domyślnie jest .appendChild()funkcją. Możesz zastąpić .appendChild()funkcję bieżącego obiektu i zaimplementować w nim obserwatora. Teraz, po zastąpieniu naszej .appendChild()funkcji, musimy pożyczyć tę funkcję od innego obiektu, aby móc dołączyć treść. Dlatego dzwonimy do .appendChild()innego div, aby w końcu dołączyć treść. Oczywiście dotyczy to również .removeChild().

var obj = document.getElementById("mydiv");
    obj.appendChild = function(node) {
        alert("changed!");

        // call the .appendChild() function of some other div
        // and pass the current (this) to let the function affect it.
        document.createElement("div").appendChild.call(this, node);
        }
    };

Tutaj możesz znaleźć naiwny przykład. Myślę, że możesz go przedłużyć samodzielnie. http://jsfiddle.net/RKLmA/31/

Nawiasem mówiąc: pokazuje to, że JavaScript jest zgodny z założeniem OpenClosed. :)

Andries
źródło
Nie działa z dołączonym dzieckiem ... Właściwie modyfikuję jego HTML za pomocą innych funkcji.
BoqBoq
Jak removeChild () replaceChild () itp. Ale masz rację na innerHTML. Powinieneś jakoś tego uniknąć.
Andries