Événement jQuery: Détecter les modifications apportées au html / texte d'un div

266

J'ai un div qui a son contenu change tout le temps, que ce soit ajax requests, jquery functions, bluretc , etc.

Existe-t-il un moyen de détecter les changements sur ma div à tout moment?

Je ne veux utiliser aucun intervalle ou valeur par défaut vérifiée.

Quelque chose comme ça ferait

$('mydiv').contentchanged() {
 alert('changed')
}
BoqBoq
la source
14
@Rob Cela lie un keypressévénement à un <div>élément modifiable . Je ne suis pas sûr que les solutions y s'appliquent. Ils n'accepteraient certainement aucune modification programmatique du contenu d'un élément.
Anthony Grist

Réponses:

409

Si vous ne voulez pas utiliser la minuterie et vérifier innerHTML, vous pouvez essayer cet événement

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

Plus de détails et les données de support du navigateur sont ici .

Attention: dans les versions plus récentes de jQuery, bind () est obsolète, vous devez donc utiliser on () à la place:

$('body').on('DOMSubtreeModified', 'mydiv', function(){
  console.log('changed');
});
iman
la source
14
Gardez à l'esprit que DOMSubtreeModified n'est pas pris en charge dans IE8 (et ci-dessous).
Gavin
56
cet événement est déconseillé w3.org/TR/DOM-Level-3-Events/#glossary-deprecated
Isaiyavan Babu Karan
3
Mozilla 33: tombé en récursivité pour l'élément <body>. Besoin de trouver un autre moyen
Chaki_Black
17
C'est sérieux N'UTILISEZ PAS cet événement, il plantera tout votre travail car il est renvoyé tout le temps. Utilisez plutôt les événements ci-dessous $ ('. MyDiv'). Bind ('DOMNodeInserred DOMNodeRemoved', function () {});
George SEDRA
19
Cette méthode est obsolète! Utilisez plutôt:$("body").on('DOMSubtreeModified', "mydiv", function() { });
Asif
55

Utilisation de 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
la source
6
C'est la bonne réponse car elle est désormais préférée à l'utilisation de DOMSubtreeModified
Joel Davey
3
Je reçois une erreur avec cela, même si j'ai donné le bon sélecteur. "VM21504: 819 TypeError non intercepté: échec de l'exécution de 'observe' sur 'MutationObserver': le paramètre 1 n'est pas de type 'Node'."
samir
54

Puisque $("#selector").bind()est obsolète , vous devez utiliser:

$("body").on('DOMSubtreeModified', "#selector", function() {
    // code here
});
Petar Nikov
la source
1
C'était le facteur de différenciation, le complément #pour indiquer que le symbole devait passer avant le sélecteur.
Chris - Jr
42

Vous pouvez essayer ceci

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

});

mais cela pourrait ne pas fonctionner dans Internet Explorer, je ne l'ai pas testé

user1790464
la source
3
REMARQUE!: Si vous l'utilisez pour un déclencheur et que la page subit de nombreuses modifications - elle exécutera votre fonction X fois (peut-être même X = 1000 ou plus), ce qui pourrait être très inefficace. Une solution simple consiste à définir une variable booléenne "en cours d'exécution", qui ... if (running == true) {return} ... sans exécuter votre code s'il est déjà en cours d'exécution. Définissez running = true juste après votre logique if et running = false avant la fermeture de votre fonction. Vous pouvez également utiliser une minuterie pour limiter votre fonction afin qu'elle ne puisse s'exécuter que toutes les X secondes. running = true; setTimeout (function () {running = false}, 5000); (ou quelque chose de mieux)
JxAxMxIxN
J'ai utilisé cela sur une boîte de sélection qui avait des options ajoutées et supprimées. Cela a très bien fonctionné lorsque des éléments ont été ajoutés, mais la suppression semblait être à 1 élément derrière. Lorsque la dernière option a été supprimée, elle ne se déclenche pas.
CodeMonkey
2
@JxAxMxIxN Vous pouvez également augmenter le délai d'expiration en effaçant et en définissant à nouveau le délai:clearTimeout(window.something); window.something = setTimeout(...);
Ctrl-C
d'accord - votre chemin est le chemin à parcourir - depuis l'apprentissage de Python, j'ai éliminé beaucoup de mes mauvaises pratiques de codage dans plusieurs langues (pas toutes, juste beaucoup;)
JxAxMxIxN
33

Vous recherchez MutationObserver ou Mutation Events . Ni l'un ni l'autre ne sont pris en charge partout et ne sont pas considérés avec trop d'affection par le monde des développeurs.

Si vous savez (et pouvez vous assurer que) la taille du div changera, vous pourrez peut-être utiliser l' événement de redimensionnement du crossbrowser .

rodneyrehm
la source
1
C'est la bonne. Plus précisément, DOMSubtreeModified . Vous pourriez trouver la bibliothèque récapitulative de mutation utile et cette liste d' événements d'arborescence DOM .
BenjaminRH
1
cet événement est déprécié developer.mozilla.org/en-US/docs/Web/Guide/Events/…
Adrien Be
9
Au cas où quelqu'un d'autre aurait dû essayer de tout lire partout, c'est la bonne réponse. Les événements de mutation étaient pris en charge dans les anciens navigateurs, Mutation Observer est ce qui est pris en charge dans les navigateurs modernes et le sera à l'avenir. Voir le lien pour le support: CANIUSE Mutation Observer
Josh Mc
20

Le code suivant fonctionne pour moi.

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

J'espère que cela aidera quelqu'un :)

Sanchit Gupta
la source
C'est la même réponse que celle de @Artley
Black
@Black Merci! Je viens de vérifier la réponse d'Artley. Je vais m'en occuper la prochaine fois.
Sanchit Gupta
17

Il n'y a pas de solution intégrée à ce problème, c'est un problème avec votre modèle de conception et de codage.

Vous pouvez utiliser le modèle éditeur / abonné. Pour cela, vous pouvez utiliser des événements personnalisés jQuery ou votre propre mécanisme d'événements.

Première,

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

Vous pouvez maintenant vous abonner aux événements divhtmlchanging / divhtmlchanged comme suit,

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

Maintenant, vous devez changer vos modifications de contenu div via cette changeHtml()fonction. Vous pouvez donc surveiller ou effectuer les modifications nécessaires en conséquence, car l'argument de données de rappel de liaison contenant les informations.

Vous devez changer le code HTML de votre div comme ceci;

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

Et aussi, vous pouvez l'utiliser pour tout élément html à l'exception de l'élément d'entrée. Quoi qu'il en soit, vous pouvez le modifier pour l'utiliser avec n'importe quel élément.

Sameera Millavithanachchi
la source
Pour observer et agir sur les modifications d'un élément particulier, modifiez simplement la fonction changeHtml pour utiliser 'elem.trigger (...)' au lieu de 'jQuery.event.trigger (...)', puis liez à l'élément comme $ ('# my_element_id'). on ('htmlchanged', fonction (e, data) {...}
KenB
8
"c'est un problème avec votre modèle de conception et de codage", que faire si vous incluez des scripts tiers donc vous n'avez aucun contrôle sur leur code source? mais vous devez détecter leurs changements à une div?
DrLightman
@DrLightman une règle de base est de choisir une bibliothèque tierce avec un événement de rappel fourni
Marcel Djaman
8

Utilisez MutationObserver comme on le voit dans cet extrait fourni par Mozilla , et adapté de ce billet de blog

Alternativement, vous pouvez utiliser l'exemple JQuery vu dans ce lien

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
la source
3

Vous pouvez stocker l'ancien innerHTML du div dans une variable. Définissez un intervalle pour vérifier si l'ancien contenu correspond au contenu actuel. Quand ce n'est pas vrai, faites quelque chose.

amour de code
la source
1

Essayez le MutationObserver:

prise en charge du navigateur: 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
la source
0

L'ajout de contenu à une div, que ce soit via jQuery ou via de DOM-API directement, revient par défaut à la .appendChild()fonction. Ce que vous pouvez faire est de remplacer la .appendChild()fonction de l'objet actuel et d'y implémenter un observateur. Maintenant que nous avons outrepassé notre .appendChild()fonction, nous devons emprunter cette fonction à un autre objet pour pouvoir ajouter le contenu. Pour cela, nous appelons .appendChild()un autre div pour finalement ajouter le contenu. Bien sûr, cela vaut également pour le .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);
        }
    };

Vous trouverez ici un exemple naïf. Vous pouvez l'étendre par vous-même, je suppose. http://jsfiddle.net/RKLmA/31/

Soit dit en passant: cela montre que JavaScript est conforme au principe OpenClosed. :)

Andries
la source
Cela ne fonctionne pas avec append child ... J'en modifie en fait le html via d'autres fonctions.
BoqBoq
Comme removeChild () replaceChild () etc. Mais vous avez raison sur innerHTML. Vous devriez l'éviter en quelque sorte.
Andries