用户脚本在执行代码技术之前要等待页面加载?


78

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

问题是,此特定页面有时需要花费一些时间才能加载所有内容。

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

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

在Greasemonkey中使用哪种最佳技术来确保在页面完成加载后将执行特定代码?


2
您可以(function init(){var counter = document.getElementById('id-of-element');if (counter) { /* do something with counter element */ } else { setTimeout(init, 0);}})();用来连续轮询元素的存在。那是最通用的解决方案。
罗布W

2
Greasemonkey的表亲Tampermonkey和Scriptish支持更多的@run-at值,包括document-idlecontext-menu可能有用。似乎Greasemonkey仍在增加document-idle对它的支持,尽管到目前为止还没有记录。
骆马先生2015年

对我有用的@RobW,谢谢。
zx81

Answers:


70

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

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

在您的用户脚本中


16
添加jQuery就像添加@require http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js到当前脚本一样简单。我也将它们放到模板脚本中,因为我一直使用。也要this.$ = this.jQuery = jQuery.noConflict(true);避免典型的冲突。
m3nda 2014年

何时添加jQuery依赖关系
sleblanc

1
不知道为什么人们如此渴望为所有内容添加jquery。
Wyatt8740

60

这是一个普遍的问题,正如您已经说过的那样,仅等待页面加载是不够的-因为AJAX可以并且确实会在那之后改变事情。

在这些情况下,有一个标准(实用)可靠的实用程序。它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
}

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


如果目标页面上已经装有JQuery怎么办?在页面上加载JQuery会破坏原始代码,但是我的GM脚本会在JQuery运行之前运行。我的方案的特定页面是Backpack.tf,它正在运行JQuery 1.7.2。
2014年

4
@Jack,在脚本中使用jQuery不会将其加载到页面中,也不会在您@grant GM_addStyle如图所示使用时破坏任何内容。那就是它的目的。
Brock Adams

请确保在传递true的第三个参数waitForKeyElements,如果你只希望你的代码运行一次
Starwarswii

39

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

// @run-at      document-idle

文档描述如下:

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


7
警告。Greasemonkey的实现似乎与Chrome / Tampermonkey的实现不同。因此,脚本在浏览器之间可能无法正常工作。使用类似waitForKeyElements或的功能更强大MutationObserver
布罗克·亚当斯

19

Brock的回答很好,但是我想为AJAX问题提供另一个更现代,更优雅的解决方案。由于他的脚本还用于setInterval()定期检查(300毫秒),因此无法立即响应。

如果需要立即响应,则可以使用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更改频繁或您的条件需要很长时间才能评估,则这可能会很慢。

另一个用例是,如果您不查找任何特定元素,而只是等待页面停止更改。您也可以结合使用它setTimeout()来等待。

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
}

这种方法用途广泛,您也可以侦听属性和文本更改。只需设置attributescharacterDatatrue在选项

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

13

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

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

如果是这样,Brock Adams的这段漂亮代码可以为您提供帮助:https :
//gist.github.com/raw/2625891/waitForKeyElements.js

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

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


6

如果要操纵节点,例如获取节点的值或更改样式,则可以使用此功能等待这些节点

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


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

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.