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é.
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
siattributes
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
sicharacterData
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.)
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 .
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.
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:
pjax:end
sur document
utilisé par de nombreux sites basés sur pjax, par exemple. GitHub,message
sur window
utilisé par exemple. Recherche Google dans le navigateur Chrome,spfdone
sur document
utilisé par Youtube, 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();
});
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();
}
});
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.
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);
}
_