web-dev-qa-db-fra.com

Existe-t-il un auditeur de modification JavaScript / jQuery DOM?

En gros, je veux qu'un script s'exécute lorsque le contenu d'un DIV change. Étant donné que les scripts sont séparés (script de contenu dans le script d'extension Chrome et de page Web), il me faut un moyen d'observer simplement les modifications de l'état DOM. Je pourrais mettre en place des sondages mais cela semble bâclé.

370
Fletcher Moore

Plusieurs années plus tard, il existe officiellement une meilleure solution. Les observateurs de mutation DOM4 remplacent les événements déconseillés de la mutation DOM3. Ils sont actuellement implémentés dans les navigateurs modernes en tant que MutationObserver (ou en tant que préfixe du vendeur WebKitMutationObserver dans les anciennes versions de 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
  //...
});

Cet exemple écoute les modifications du DOM sur document et son sous-arbre entier. Il se déclenche également lorsque des modifications sont apportées aux attributs d'éléments, ainsi que des modifications structurelles. Le brouillon de spécification contient la liste complète des propriétés du programme d'écoute de mutation valides :

childList

  • Réglez sur true si des mutations sur les enfants de la cible doivent être observées.

attributs

  • Réglez sur true si des mutations des attributs de la cible doivent être observées.

characterData

  • Réglez sur true si des mutations des données de la cible doivent être observées.

sous-arbre

  • Réglez sur true si les mutations doivent non seulement être ciblées, mais également leurs descendants.

attributeOldValue

  • Défini sur true si attributes est défini sur true et que la valeur de l'attribut de la cible avant l'enregistrement de la mutation doit être enregistrée.

characterDataOldValue

  • Défini sur true si characterData est défini sur true et que les données de la cible avant l'enregistrement de la mutation doivent être enregistrées.

attributeFilter

  • Définissez une liste de noms d'attributs locaux (sans espace de nom) si toutes les mutations d'attribut ne doivent pas être observées.

(Cette liste est à jour en avril 2014; vous pouvez vérifier les spécifications pour tout changement.)

450
apsillers

Modifier

Cette réponse est maintenant obsolète. Voir la réponse par apsillers .

Comme il s’agit d’une extension Chrome, vous pouvez également utiliser l’événement DOM standard - DOMSubtreeModified. Voir le support pour ceci event sur les navigateurs. Il est pris en charge dans Chrome depuis 1.0.

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

Voir un exemple de travail ici .

203
Anurag

De nombreux sites utilisent AJAX pour ajouter/afficher/modifier du contenu de manière dynamique. Il est parfois utilisé à la place de la navigation sur le site. Ainsi, l'URL actuelle est modifiée par programme et les scripts de contenu ne sont pas automatiquement exécutés par le navigateur, car la page n'est pas entièrement extraite du serveur distant.


Méthodes JS habituelles de détection des modifications de page disponibles dans un script de conten .

  • MutationObserver ( docs ) pour détecter littéralement les modifications du DOM:

  • Écouteur d'événements pour les sites signalant une modification de contenu en envoyant un événement DOM:

  • Vérification périodique du DOM via setInterval :
    Évidemment, cela ne fonctionnera que dans les cas où vous attendez qu'un élément spécifique identifié par son identifiant/sélecteur apparaisse, et cela ne vous permettra pas de détecter universellement le nouveau contenu ajouté de manière dynamique à moins d'inventer une sorte d'empreinte digitale de l'existant. Contenu.

  • Cloaking API d'historique dans un script DOM injecté :

    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();
    });
    
  • Écoute de hashchange , popstate événements:

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



Spécifique aux extensions: détecte les modifications d'URL dans arrière-plan / page d'événement .

Il existe des API avancées pour travailler avec la navigation: webNavigation , webRequest , mais nous utiliserons un simple chrome.tabs.onUpdated écouteur d'événements qui - envoie un message au script de contenu:

  • manifest.json:
    declare background/event page
    declare script de conten
    add "tabs"permission .

  • 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();
        }
    });
    
41
wOxxOm

Une autre approche dépend de la façon dont vous modifiez la div. Si vous utilisez JQuery pour modifier le contenu d'un div avec sa méthode html (), vous pouvez étendre cette méthode et appeler une fonction d'enregistrement chaque fois que vous insérez du code HTML dans un 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 );

Nous interceptons simplement les appels à html (), appelons une fonction d’enregistrement avec cela, qui dans le contexte fait référence à l’élément cible qui obtient un nouveau contenu, puis nous passons l’appel à la fonction jquery.html () originale. N'oubliez pas de renvoyer les résultats de la méthode html () d'origine, car JQuery l'attend pour le chaînage de méthodes.

Pour plus d'informations sur le remplacement et l'extension de méthode, consultez la page http://www.bennadel.com/blog/2009-Using-Self-Executing-Function-Arguments-To-Override-Core-jQuery-Methods.htm , c’est là que j’ai écrit la fonction de fermeture. Consultez également le didacticiel des plugins sur le site de JQuery.

27
Zac Imboden

En plus des outils "bruts" fournis par MutationObserver API , il existe des bibliothèques "de confort" pour travailler avec des mutations DOM.

Considérez: MutationObserver représente chaque changement de DOM dans les sous-arbres. Ainsi, si vous attendez, par exemple, qu'un certain élément soit inséré, il se peut qu'il soit profondément à l'intérieur des enfants de _mutations.mutation[i].addedNodes[j]_.

Un autre problème réside dans le fait que votre propre code, en réaction aux mutations, change de DOM - vous souhaitez souvent le filtrer.

Une bonne bibliothèque pratique qui résout ces problèmes est mutation-summary (disclaimer: je ne suis pas l'auteur, mais un utilisateur satisfait), ce qui vous permet de spécifier des requêtes sur ce qui vous intéresse et obtenez exactement cela.

Exemple d'utilisation basique tiré de la documentation:

_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);
}
_
6
Xan