Userscript, um auf das Laden der Seite zu warten, bevor Codetechniken ausgeführt werden?

78

Ich schreibe ein Greasemonkey-Benutzerskript und möchte, dass der spezifische Code ausgeführt wird, wenn die Seite vollständig geladen ist, da er eine Div-Anzahl zurückgibt, die angezeigt werden soll.

Das Problem ist, dass diese bestimmte Seite manchmal etwas dauert, bis alles geladen ist.

Ich habe versucht, zu dokumentieren $(function() { });und zu $(window).load(function(){ });verpacken. Allerdings scheint keiner für mich zu funktionieren, obwohl ich sie möglicherweise falsch anwende.

Das Beste, was ich tun kann, ist eine zu verwenden, setTimeout(function() { }, 600);die funktioniert, obwohl sie nicht immer zuverlässig ist.

Was ist die beste Technik in Greasemonkey, um sicherzustellen, dass der spezifische Code ausgeführt wird, wenn die Seite vollständig geladen ist?

Myne Mai
quelle
2
Sie können verwenden, (function init(){var counter = document.getElementById('id-of-element');if (counter) { /* do something with counter element */ } else { setTimeout(init, 0);}})();um kontinuierlich nach der Existenz des Elements zu suchen. Das ist die allgemeinste Lösung.
Rob W
2
Die Cousins ​​von Greasemonkey, Tampermonkey und Scriptish, unterstützen mehr @run-atWerte, die enthalten document-idleund context-menuvon Nutzen sein können. Es scheint auch , dass Greasemonkey ist Unterstützung Zugabe für document-idleobwohl es jetzt noch nicht dokumentiert worden.
Herr Lama
@RobW das hat bei mir funktioniert, danke.
zx81

Antworten:

70

Greasemonkey hat (normalerweise) kein jQuery. Der übliche Ansatz ist also die Verwendung

window.addEventListener('load', function() {
    // your code here
}, false);

in Ihrem Userscript

devnull69
quelle
16
Das Hinzufügen von jQuery ist ganz einfach, da Sie @require http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.jses Ihrem aktuellen Skript hinzufügen . Ich habe sie auch in das Vorlagenskript eingefügt, weil ich sie immer benutze. Auch this.$ = this.jQuery = jQuery.noConflict(true);um typische Konflikte zu vermeiden.
m3nda
Hinzufügen von Abhängigkeiten zu jQuery, wenn
sleblanc
1
Ich bin mir nicht sicher, warum die Leute so eifrig sind, für alles Fragen zu stellen.
Wyatt8740
60

Dies ist ein häufiges Problem, und wie Sie bereits gesagt haben, reicht es nicht aus, auf das Laden der Seite zu warten, da AJAX die Dinge noch lange danach ändern kann und tut.

Für diese Situationen gibt es ein standardmäßiges (ish) robustes Dienstprogramm. Es ist das waitForKeyElements()Dienstprogramm .

Verwenden Sie es so:

// ==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
}

Geben Sie genaue Details Ihrer Zielseite für ein genaueres Beispiel an.

Brock Adams
quelle
Was ist, wenn auf der Zielseite bereits JQuery vorhanden ist? Das Laden von JQuery auf der Seite bricht den ursprünglichen Code, aber mein GM-Skript wird ausgeführt, bevor JQuery ausgeführt wird. Die spezifische Seite für mein Szenario ist backpack.tf, auf der JQuery 1.7.2 ausgeführt wird.
Jack
4
@Jack, Wenn Sie jQuery im Skript verwenden, wird es weder in die Seite geladen, noch wird etwas beschädigt, wenn Sie das @grant GM_addStylewie gezeigt verwenden. Dafür ist es da.
Brock Adams
Stellen Sie sicher, dass Sie trueals drittes Argument angeben, waitForKeyElementsob Ihr Code nur einmal ausgeführt werden soll
Starwarswii,
39

Ab Greasemonkey 3.6 (20. November 2015) unterstützt der Metadatenschlüssel @run-atden neuen Wert document-idle. Fügen Sie dies einfach in den Metadatenblock Ihres Greasemonkey-Skripts ein:

// @run-at      document-idle

Die Dokumentation beschreibt es wie folgt:

Das Skript wird ausgeführt, nachdem die Seite und alle Ressourcen (Bilder, Stylesheets usw.) geladen und Seitenskripte ausgeführt wurden.

Leviathan
quelle
7
Warnung. Greasemonkey scheint dies anders implementiert zu haben als Chrome / Tampermonkey. Daher funktionieren Skripte möglicherweise nicht in allen Browsern gleich. Es ist noch robuster, so etwas wie waitForKeyElementsoder zu verwenden MutationObserver.
Brock Adams
19

Brocks Antwort ist gut, aber ich möchte eine andere Lösung für das AJAX-Problem anbieten, die moderner und eleganter ist. Da sein Skript auch setInterval()zur regelmäßigen Überprüfung (300 ms) verwendet wird, kann es nicht sofort reagieren.

Wenn Sie sofort reagieren müssen, können Sie MutationObserver()auf DOM-Änderungen warten und darauf reagieren, sobald das Element erstellt wurde

(new MutationObserver(check)).observe(document, {childList: true, subtree: true});

function check(changes, observer) {
    if(document.querySelector('#mySelector')) {
        observer.disconnect();
        // code
    }
}

Obwohl seitdem check() bei jeder einzelnen DOM-Änderung ein Brand ausgelöst wird, kann dies langsam sein, wenn sich das DOM sehr häufig ändert oder die Bewertung Ihres Zustands lange dauert.

Ein weiterer Anwendungsfall ist, wenn Sie nicht nach einem bestimmten Element suchen, sondern nur darauf warten, dass sich die Seite nicht mehr ändert. Sie können dies mit kombinieren setTimeout(), um auch darauf zu warten.

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
}

Diese Methode ist so vielseitig, dass Sie auch auf Attribut- und Textänderungen achten können. Gerade gesetzt attributesund characterDatazu truein den Optionen

observer.observe(document, {childList: true, attributes: true, characterData: true, subtree: true});
Goweon
quelle
13

Das Einpacken meiner Skripte ist $(window).load(function(){ })für mich nie gescheitert.

Möglicherweise ist Ihre Seite fertig, aber es werden noch einige Ajax-Inhalte geladen.

In diesem Fall kann Ihnen dieser nette Code von Brock Adams helfen:
https://gist.github.com/raw/2625891/waitForKeyElements.js

Ich benutze es normalerweise, um nach Elementen zu suchen , die auf dem Postback erscheinen .

benutze es so: waitForKeyElements("elementtowaitfor", functiontocall)

RASG
quelle
6

Wenn Sie Knoten bearbeiten möchten, z. B. den Wert von Knoten abrufen oder den Stil ändern möchten, können Sie mit dieser Funktion auf diese Knoten warten

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

dann benutze 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
})

oder verwenden 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
})()

Hier ist ein Beispiel icourse163_enhance

bilabila
quelle
2

Um festzustellen, ob das Laden des XHR auf der Webseite abgeschlossen ist, wird eine Funktion ausgelöst. Ich erhalte dies von Wie verwende ich JavaScript, um "XHR fertig geladen" -Nachrichten in der Konsole in Chrome zu speichern? und es funktioniert wirklich.

    //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.
    ///programming/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;
    }

Aber wenn die Seite viele XHRs enthält, habe ich keine Ahnung, wie die bestimmte XHR gefiltert werden soll.

Eine andere Methode ist waitForKeyElements (), was sehr schön ist. https://gist.github.com/BrockA/2625891
Es gibt ein Beispiel für die Verwendung von Greasemonkey. Führen Sie das Greasemonkey-Skript mehrmals auf derselben Seite aus?

Dave B.
quelle