Existe-t-il un écouteur de changement JavaScript/jQuery DOM?

Essentiellement, je veux qu'un script s'exécute lorsque le contenu D'un DIV change. Puisque les scripts sont séparés (script de contenu dans l'extension chrome et le script de page Web), j'ai besoin d'un moyen d'observer simplement les changements dans L'état DOM. Je pourrais mettre en place des sondages mais cela semble bâclé.

342
demandé sur Xan 2010-05-16 20:43:10

5 réponses

Plusieurs années plus tard, il y a maintenant officiellement une meilleure solution. Les Observateurs de Mutation DOM4 {[14] } remplacent les événements de mutation dom3 obsolètes. Ils sont actuellement implémentés dans les navigateurs modernes comme MutationObserver (ou comme le préfixe fournisseur 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 DOM sur document et son sous-arbre entier, et il se déclenche sur les modifications apportées aux attributs des éléments ainsi que sur les modifications structurelles. Le projet de spécification a un plein liste des propriétés valides de l'écouteur mutation :

Liste des enfants

  • défini sur true Si des mutations chez les enfants de target doivent être observées.

Attributs

  • défini sur true Si des mutations des attributs de la cible doivent être observées.

CharacterData

  • défini sur true Si des mutations des données de target doivent être observées.

Sous-arbre

  • défini sur true Si des mutations non seulement sur la cible, mais aussi sur les descendants de la cible doivent être observées.

AttributeOldValue

  • défini sur true Si attributes est défini sur true et la valeur d'attribut de target avant que la mutation ne soit enregistrée.

CharacterDataOldValue

  • défini sur true si characterData est défini sur true et les données de la cible avant que la mutation ne nécessite pour être enregistré.

AttributeFilter

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

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

403
répondu apsillers 2015-11-20 14:03:00

Modifier

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

Comme il s'agit d'une extension Chrome, vous pouvez aussi bien utiliser L'événement DOM standard - DOMSubtreeModified. Voir la prise en charge de cet événement dans tous les navigateurs. Il a été pris en charge dans Chrome depuis 1.0.

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

Voir un exemple ici.

193
répondu Anurag 2018-09-14 02:24:29

De nombreux sites utilisent AJAX pour ajouter/afficher / modifier le contenu dynamiquement. Parfois, il est utilisé à la place de la navigation sur le site, de sorte que L'URL actuelle est modifiée par programme et que les scripts de contenu ne sont pas automatiquement exécutés par le navigateur dans ce cas, car la page n'est pas entièrement extraite du serveur distant.


Méthodes js habituelles de détection des changements de page disponibles dans un script de contenu .

  • MutationObserver (docs) littéralement détecter les DOM changements:

  • Event listener pour les sites qui signalent un changement 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 id/sélecteur apparaisse, et il ne vous laissera pas universellement détectez le nouveau contenu ajouté dynamiquement, sauf si vous inventez une sorte d'empreinte digitale du contenu existant.

  • Camouflage API 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();
    });
    
  • L'É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();
    });
    



Extensions spécifiques: détecter les changements D'URL dans un contexte / événement page.

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

  • Le Manifeste.json:
    déclarer page d'arrière-plan/événement
    déclarer script de contenu
    ajouter "tabs" permission.

  • Contexte.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');
        }
    });
    
  • Contenu.js

    chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
        if (msg === 'url-update') {
            doSomething();
        }
    });
    
26
répondu wOxxOm 2016-09-15 10:42:17

Une autre approche en fonction de la façon dont vous modifiez le 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 mettez du 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 ceci, qui dans le contexte fait référence à l'élément cible obtenant du nouveau contenu, puis nous passons l'appel à la jquery d'origine.html() fonction. Rappelez-vous de retourner le résultats de la méthode html () d'origine, car JQuery l'attend pour le chaînage de la méthode.

Pour plus d'informations sur la substitution et l'extension des méthodes, consultez http://www.bennadel.com/blog/2009-Using-Self-Executing-Function-Arguments-To-Override-Core-jQuery-Methods.htm, qui est où j'ai bercé la fonction de fermeture. Consultez également le tutoriel sur les plugins sur le site de JQuery.

24
répondu Zac Imboden 2012-06-04 00:47:37

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

Considérons: MutationObserver représente chaque changement DOM en termes de sous-arbres. Donc, si vous attendez, par exemple, qu'un certain élément soit inséré, il peut être profondément à l'intérieur des enfants de mutations.mutation[i].addedNodes[j].

Un autre problème est lorsque votre propre code, en réaction à des mutations, change DOM - vous voulez souvent le filtrer.

Une bonne commodité bibliothèque qui résout de tels problèmes est mutation-summary (avertissement: Je ne suis pas l'auteur, juste un utilisateur satisfait), ce qui vous permet de spécifier des requêtes de ce qui vous intéresse, et d'obtenir exactement cela.

Exemple D'utilisation de base des documents:

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);
}
3
répondu Xan 2016-05-10 12:00:26