J'écris un script utilisateur Greasemonkey et je veux qu'un code spécifique soit exécuté lorsque le chargement de la page est complètement terminé, car il renvoie le nombre de div que je veux afficher.
Le problème est que cette page particulière prend parfois un peu avant que tout soit chargé.
J'ai déjà essayé de documenter les wrappers $(function() { });
et $(window).load(function(){ });
. Cependant, aucun ne semble fonctionner pour moi, bien que je puisse les appliquer mal.
Le mieux que je puisse faire est d’utiliser un setTimeout(function() { }, 600);
qui fonctionne, bien que pas toujours fiable.
Quelle est la meilleure technique à utiliser dans Greasemonkey pour s’assurer que du code spécifique sera exécuté une fois le chargement de la page terminé?
Greasemonkey (généralement) n'a pas jQuery. Donc, l’approche commune consiste à utiliser
window.addEventListener('load', function() {
// your code here
}, false);
dans votre script utilisateur
Il s'agit d'un problème courant et, comme vous l'avez dit, attendre le chargement de la page ne suffit pas - car AJAX peut et va changer les choses longtemps après.
Il existe un utilitaire standard (ish) robuste pour ces situations. C'est l'utilitaire waitForKeyElements()
.
Utilisez-le comme suit:
// ==UserScript==
// @name _Wait for delayed or AJAX page load
// @include http://YOUR_SERVER.COM/YOUR_PATH/*
// @require http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
// @require https://Gist.github.com/raw/2625891/waitForKeyElements.js
// @grant GM_addStyle
// ==/UserScript==
/*- The @grant directive is needed to work around a major design
change introduced in GM 1.0.
It restores the sandbox.
*/
waitForKeyElements ("YOUR_jQUERY_SELECTOR", actionFunction);
function actionFunction (jNode) {
//-- DO WHAT YOU WANT TO THE TARGETED ELEMENTS HERE.
jNode.css ("background", "yellow"); // example
}
Donnez des détails exacts sur votre page cible pour un exemple plus spécifique.
À partir de Greasemonkey 3.6 (20 novembre 2015), la clé de métadonnées @run-at
prend en charge la nouvelle valeur document-idle
. Il vous suffit de placer ceci dans le bloc de métadonnées de votre script Greasemonkey:
// @run-at document-idle
La documentation la décrit comme suit:
Le script sera exécuté après la page et toutes les ressources (images, feuilles de style, etc.) seront chargées et les scripts de page exécutés.
envelopper mes scripts dans $(window).load(function(){ })
n'a jamais échoué pour moi.
peut-être que votre page est terminée, mais qu'il reste du contenu ajax en cours de chargement.
si tel est le cas, ce joli morceau de code de Brock Adams peut vous aider:
https://Gist.github.com/raw/2625891/waitForKeyElements.js
je l'utilise généralement pour surveiller les éléments qui apparaissent sur postback.
utilisez-le comme ceci: waitForKeyElements("elementtowaitfor", functiontocall)
Si vous voulez manipuler des noeuds, comme obtenir une valeur de noeud ou changer de style, vous pouvez attendre que ces noeuds utilisent cette fonction
const waitFor = (...selectors) => new Promise(resolve => {
const delay = 500
const f = () => {
const elements = selectors.map(selector => document.querySelector(selector))
if (elements.every(element => element != null)) {
resolve(elements)
} else {
setTimeout(f, delay)
}
}
f()
})
puis utilisez promise.then
// scripts don't manipulate nodes
waitFor('video', 'div.sbg', 'div.bbg').then(([video, loading, videoPanel])=>{
console.log(video, loading, videoPanel)
// scripts may manipulate these nodes
})
ou utilisez async&await
//this semicolon is needed if none at end of previous line
;(async () => {
// scripts don't manipulate nodes
const [video, loading, videoPanel] = await waitFor('video','div.sbg','div.bbg')
console.log(video, loading, video)
// scripts may manipulate these nodes
})()
Voici un exemple icourse163_enhance
La réponse de Brock est bonne, mais j'aimerais proposer une autre solution au problème de AJAX, par souci d'exhaustivité. Comme son script utilise également setInterval()
pour vérifier périodiquement (300 ms), il ne peut pas répondre instantanément.
Si vous devez répondre immédiatement, vous pouvez utiliser MutationObserver()
pour écouter les modifications du DOM et y répondre dès la création de l'élément.
(new MutationObserver(check)).observe(document, {childList: true, subtree: true});
function check(changes, observer) {
if(document.querySelector('#mySelector')) {
observer.disconnect();
// code
}
}
Bien que check()
se déclenche à chaque changement de DOM, cela peut être lent si le DOM change très souvent ou si votre condition met longtemps à être évaluée.
Un autre cas d'utilisation est si vous ne recherchez aucun élément spécifique, mais attendez simplement que la page cesse de changer. Vous pouvez combiner cela avec setTimeout()
pour attendre aussi.
var observer = new MutationObserver(resetTimer);
var timer = setTimeout(action, 3000, observer); // wait for the page to stay still for 3 seconds
observer.observe(document, {childList: true, subtree: true});
function resetTimer(changes, observer) {
clearTimeout(timer);
timer = setTimeout(action, 3000, observer);
}
function action(o) {
o.disconnect();
// code
}
Cette méthode est si polyvalente que vous pouvez également écouter les modifications d’attributs et de texte. Il suffit de mettre attributes
et characterData
à true
dans les options
observer.observe(document, {childList: true, attributes: true, characterData: true, subtree: true});
Pour détecter si le chargement XHR est terminé dans la page Web, une fonction est déclenchée . Je reçois ce message depuis Comment utiliser JavaScript pour stocker les messages "Le chargement terminé XHR" dans la console dans Chrome? et ça marche vraiment.
//This overwrites every XHR object's open method with a new function that adds load and error listeners to the XHR request. When the request completes or errors out, the functions have access to the method and url variables that were used with the open method.
//You can do something more useful with method and url than simply passing them into console.log if you wish.
//https://stackoverflow.com/questions/43282885/how-do-i-use-javascript-to-store-xhr-finished-loading-messages-in-the-console
(function() {
var origOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url) {
this.addEventListener('load', function() {
console.log('XHR finished loading', method, url);
display();
});
this.addEventListener('error', function() {
console.log('XHR errored out', method, url);
});
origOpen.apply(this, arguments);
};
})();
function display(){
//codes to do something;
}
Mais s'il y a beaucoup de XHR dans la page, je ne sais pas comment filtrer le XHR défini.
WaitForKeyElements () est une autre méthode, Nice . https://Gist.github.com/BrockA/2625891
Il existe un exemple d'utilisation de Greasemonkey . Exécuter le script Greasemonkey sur la même page, plusieurs fois?