Evento jQuery: rileva modifiche al codice html / di un div


266

Ho un div che ha il suo contenuto di cambiare tutto il tempo, sia esso ajax requests, jquery functions, blurecc ecc

C'è un modo per rilevare eventuali cambiamenti sul mio div in qualsiasi momento?

Non desidero utilizzare alcun intervallo o il valore predefinito verificato.

Qualcosa del genere farebbe

$('mydiv').contentchanged() {
 alert('changed')
}


14
@Rob Questo è un keypressevento vincolante per un <div>elemento contenteditable . Non sono sicuro che le soluzioni siano applicabili a questo. Sicuramente non avrebbero rilevato alcuna modifica programmatica al contenuto di un elemento.
Anthony Grist,

Risposte:


409

Se non si desidera utilizzare il timer e controllare innerHTML, è possibile provare questo evento

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

Maggiori dettagli e dati di supporto del browser sono qui .

Attenzione: nelle versioni jQuery più recenti bind () è deprecato, quindi dovresti usare invece su ():

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

14
Tieni presente che DOMSubtreeModified non è supportato in IE8 (e sotto).
Gavin,


3
Mozilla 33: fallito nella ricorsione per l'elemento <body>. Necessario per trovare un altro modo
Chaki_Black,

17
È serio NON utilizzare questo evento, si bloccherà tutto il tuo lavoro perché viene sempre attivato. Usa invece gli eventi sotto $ ('. MyDiv'). Bind ('DOMNodeInserted DOMNodeRemoved', function () {});
George SEDRA,

19
Questo metodo è stato deprecato! Invece usa:$("body").on('DOMSubtreeModified', "mydiv", function() { });
Asif

55

Utilizzo di 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);

6
Questa è la risposta corretta poiché ora è favorita dall'utilizzo di DOMSubtreeModified
Joel Davey,

3
Ricevo un errore, anche se ho fornito il selettore corretto. "VM21504: 819 TypeError non rilevato: Impossibile eseguire 'osservare' su 'MutationObserver': il parametro 1 non è di tipo 'Nodo'."
Samir,


42

Puoi provare questo

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

});

ma questo potrebbe non funzionare in Internet Explorer, non l'ho testato



3
NOTA !: se lo usi per un trigger e vengono apportate molte modifiche alla pagina, eseguirà la tua funzione X volte (forse anche X = 1.000 o più) che potrebbe essere molto inefficiente. Una soluzione semplice è definire una var booleana "in esecuzione", che ... if (running == true) {return} ... senza eseguire il codice se è già in esecuzione. Impostare running = true subito dopo la logica if e running = false prima che la funzione venga chiusa. È inoltre possibile utilizzare un timer per limitare la funzione per poter eseguire solo ogni X secondi. esecuzione = true; setTimeout (function () {} in esecuzione = false, 5000); (o qualcosa di meglio)
JxAxMxIxN

L'ho usato su una casella selezionata in cui sono state aggiunte e rimosse opzioni. Ha funzionato alla grande quando sono stati aggiunti degli elementi, ma la rimozione sembrava essere 1 elemento dietro. Quando l'ultima opzione è stata rimossa non si attiva.
CodeMonkey

2
@JxAxMxIxN Puoi anche annullare il timeout cancellando e impostando di nuovo il timeout:clearTimeout(window.something); window.something = setTimeout(...);
Ctrl-C

d'accordo - la tua strada è la strada da percorrere - da quando ho imparato Python ho chiarito molte delle mie povere pratiche di programmazione in più lingue (non tutte, solo molte;)
JxAxMxIxN

33

Stai cercando MutationObserver o Mutation Events . Né sono supportati dappertutto né sono considerati con affetto dal mondo degli sviluppatori.

Se conosci (e puoi assicurarti che) le dimensioni del div cambieranno, potresti essere in grado di usare l' evento di ridimensionamento del crossbrowser .


1
Questo è quello. In particolare, DOMSubtreeModified . È possibile trovare utile la libreria di riepilogo delle mutazioni e questo elenco di eventi dell'albero DOM .
BenjaminRH,


9
Nel caso in cui qualcun altro abbia dovuto provare a leggere tutto ovunque, questa è la risposta corretta. Gli eventi di mutazione erano supportati nei browser precedenti, Mutation Observer è ciò che sarà supportato nei browser moderni e sarà supportato in futuro. Vedi link per supporto: CANIUSE Mutation Observer
Josh Mc

20

Il seguente codice funziona per me.

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

Spero che possa aiutare qualcuno :)


Questa è la stessa risposta di quella di @Artley
Black

@ Black Grazie! Ho appena controllato la risposta di Artley. Mi prenderò cura di questo la prossima volta.
Sanchit Gupta,

17

Non esiste una soluzione integrata a questo problema, questo è un problema con la progettazione e il modello di codifica.

È possibile utilizzare il modello editore / abbonato. Per questo è possibile utilizzare eventi personalizzati jQuery o il proprio meccanismo di eventi.

Primo,

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

Ora puoi iscriverti agli eventi divhtmlchanging / divhtmlchanged come segue,

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

Ora, è necessario modificare le modifiche al contenuto div tramite questa changeHtml()funzione. Pertanto, è possibile monitorare o apportare le modifiche necessarie di conseguenza perché associare l'argomento dei dati di callback contenente le informazioni.

Devi cambiare l'html del tuo div in questo modo;

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

Inoltre, puoi usarlo per qualsiasi elemento html tranne l'elemento di input. Comunque puoi modificarlo per usarlo con qualsiasi elemento (i).


Per osservare e agire sulle modifiche a un particolare elemento, basta modificare la funzione changeHtml per usare 'elem.trigger (...)' invece di 'jQuery.event.trigger (...)', quindi associarsi all'elemento come $ ('# my_element_id'). on ('htmlchanged', function (e, data) {...}
KenB

8
"questo è un problema con il tuo modello di progettazione e codifica", cosa fare se includi script di terze parti quindi non hai alcun controllo sul loro codice sorgente? ma devi rilevare le loro modifiche in un div?
DrLightman,

@DrLightman una regola empirica è quella di scegliere la lib di terze parti con l'evento di callback fornito
Marcel Djaman

8

Usa MutationObserver come visto in questo frammento fornito da Mozilla e adattato da questo post del blog

In alternativa, puoi usare l'esempio JQuery visto in questo link

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

3

Puoi archiviare il vecchio innerHTML del div in una variabile. Impostare un intervallo per verificare se il vecchio contenuto corrisponde al contenuto corrente. Quando questo non è vero, fai qualcosa.


1

Prova il MutationObserver:

supporto del browser: 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>


0

L'aggiunta di alcuni contenuti a un div, tramite jQuery o direttamente tramite DOM-API, per impostazione predefinita è la .appendChild()funzione. Quello che puoi fare è ignorare la .appendChild()funzione dell'oggetto corrente e implementare un osservatore in esso. Ora che abbiamo ignorato la nostra .appendChild()funzione, dobbiamo prendere in prestito quella funzione da un altro oggetto per poter aggiungere il contenuto. Perciò chiamiamo il .appendChild()di un altro div per aggiungere finalmente il contenuto. Naturalmente, questo vale anche per il .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);
        }
    };

Qui puoi trovare un esempio ingenuo. Puoi estenderlo da solo, immagino. http://jsfiddle.net/RKLmA/31/

A proposito: questo mostra che JavaScript è conforme al principio OpenClosed. :)


Non funziona con append child ... In realtà ne modifico l'html tramite altre funzioni.
BoqBoq,

Come removeChild () replaceChild () ecc. Ma hai ragione su innerHTML. Dovresti evitarlo in qualche modo.
Andries,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.