104

我正在编写一个 Greasemonkey 用户脚本,并希望在页面完全加载完成时执行特定代码,因为它返回我想要显示的 div 计数。

问题是,这个特定页面有时需要一点时间才能加载所有内容。

我已经尝试过,文档$(function() { });$(window).load(function(){ });包装器。但是,似乎没有一个对我有用,尽管我可能会错误地应用它们。

我能做的最好的就是使用一个setTimeout(function() { }, 600);可行的方法,尽管它并不总是可靠的。

在 Greasemonkey 中使用什么技术来确保在页面完成加载时执行特定代码?

4

7 回答 7

89

Greasemonkey(通常)没有 jQuery。所以常用的方法是使用

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

在您的用户脚本中

于 2012-10-15T14:38:30.987 回答
72

这是一个常见问题,正如您所说,等待页面加载是不够的——因为 AJAX 可以并且确实在此之后很长时间内改变了事情。

对于这些情况,有一个标准的(ish)强大的实用程序。这是实用waitForKeyElements()程序

像这样使用它:

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

提供目标页面的确切详细信息以获取更具体的示例。

于 2012-10-15T15:51:48.300 回答
47

从 Greasemonkey 3.6(2015 年 11 月 20 日)开始,元数据键@run-at支持新值document-idle。只需将其放在 Greasemonkey 脚本的元数据块中:

// @run-at      document-idle

文档描述如下:

该脚本将在页面和所有资源(图像、样式表等)加载且页面脚本运行后运行。

于 2017-01-15T23:04:52.433 回答
34

Brock 的回答很好,但我想为 AJAX 问题提供另一种更现代、更优雅的解决方案。

由于他的脚本和大多数其他脚本一样,也使用setInterval()定期检查(300 毫秒),因此无法立即响应并且总是存在延迟。其他解决方案使用 onload 事件,这些事件通常会比您希望在动态页面上更早触发。

您可以MutationObserver()用来监听 DOM 更改并在创建元素后立即响应它们

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

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

虽然由于check()每次 DOM 更改都会触发,但如果 DOM 经常更改或者您的条件需要很长时间来评估,这可能会很慢,因此不要使用观察document,而是尝试通过观察尽可能小的 DOM 子树来限制范围。

这种方法非常通用,可以应用于很多情况。要多次响应,请不要在触发时断开观察者的连接。

另一个用例是,如果您不寻找任何特定元素,而只是等待页面停止更改,则可以将其与计时器结合使用。

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

// reset timer every time something changes
function resetTimer(changes, observer) {
    clearTimeout(timer);
    timer = setTimeout(action, 3000, observer);
}

function action(observer) {
    observer.disconnect();
    // code
}

这种方法非常通用,您也可以监听属性和文本的变化。只需在选项中设置attributescharacterDatatrue

observer.observe(document, {childList: true, attributes: true, characterData: true, subtree: true});
于 2017-11-21T06:41:01.977 回答
14

包装我的脚本$(window).load(function(){ })对我来说从来没有失败过。

也许您的页面已经完成,但仍有一些 ajax 内容正在加载。

如果是这种情况,Brock Adams的这段代码可以帮助你:
https ://gist.github.com/raw/2625891/waitForKeyElements.js

我通常用它来监视回发中出现的元素。

像这样使用它:waitForKeyElements("elementtowaitfor", functiontocall)

于 2012-10-15T14:43:12.183 回答
7

如果要操作节点,例如获取节点的值或更改样式,可以使用此函数等待这些节点

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

然后使用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
})

或使用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
})()

这是一个示例icourse163_enhance

于 2018-03-28T14:21:14.013 回答
2

为了检测 XHR 是否在网页中完成加载,它会触发一些功能。我从如何使用 JavaScript 在 Chrome 的控制台中存储“XHR 完成加载”消息?它确实有效。

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

但是如果页面中有很多 XHR,我不知道如何过滤确定的一个 XHR。

另一种方法是 waitForKeyElements() 很好。 https://gist.github.com/BrockA/2625891
有 Greasemonkey 使用的示例。 在同一页面上多次运行 Greasemonkey 脚本?

于 2018-04-02T06:01:54.270 回答