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

Posted

技术标签:

【中文标题】在执行代码技术之前等待页面加载的用户脚本?【英文标题】:Userscript to wait for page to load before executing code techniques? 【发布时间】:2012-10-05 12:49:42 【问题描述】:

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

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

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

我能做的最好的就是使用setTimeout(function() , 600);,虽然它并不总是可靠的。

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

【问题讨论】:

您可以使用(function init()var counter = document.getElementById('id-of-element');if (counter) /* do something with counter element */ else setTimeout(init, 0);)(); 不断轮询元素是否存在。这是最通用的解决方案。 Greasemonkey 的表亲 Tampermonkey 和 Scriptish 支持更多的 @run-at 值,其中包括可能有用的 document-idlecontext-menu。似乎 Greasemonkey 是 adding support 对应于 document-idle,尽管它尚未被记录在案。 @RobW 对我有用,谢谢。 【参考方案1】:

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

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

在您的用户脚本中

【讨论】:

添加 jQuery 就像添加 @require http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js 到您当前的脚本一样简单。我也将它们放入模板脚本中,因为我总是使用。还要输入this.$ = this.jQuery = jQuery.noConflict(true); 以避免典型的冲突。 添加对jQuery的依赖时 不知道为什么人们如此渴望为所有东西添加 jquery。 人们渴望使用 jQuery 的原因是选择器。 Document.querySelector 在用户脚本中不起作用。至少就我的经验而言。 我最近几年才使用 Tampermonkey ......它的用户脚本完美支持 document.querySelector【参考方案2】:

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

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

像这样使用它:

// ==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 运行之前运行。我的场景的特定页面是packet.tf,它运行的是JQuery 1.7.2。 @Jack,在脚本中使用 jQuery 不会将其加载到页面中,也不会在您使用 @grant GM_addStyle 时破坏任何内容,如图所示。这就是它的用途。 如果您只希望代码运行一次,请确保将true 作为第三个参数传递给waitForKeyElements【参考方案3】:

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

// @run-at      document-idle

documentation 描述如下:

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

【讨论】:

警告。 Greasemonkey 的实现方式似乎与 Chrome/Tampermonkey 不同。因此脚本在浏览器中的工作方式可能不同。使用waitForKeyElementsMutationObserver 之类的东西仍然更加健壮。【参考方案4】:

Brock's answer 很好,但我想为 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

这个方法非常通用,你也可以监听属性和文本的变化。只需在选项中将attributescharacterData 设置为true

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

【讨论】:

【参考方案5】:

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

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

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

我通常用它来监控出现在postback上的元素。

像这样使用它: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

【讨论】:

【参考方案7】:

为了检测 XHR 是否在网页中完成加载,它会触发一些功能。 我从How do I use javascript to store "XHR finished loading" messages in the console in Chrome? 得到这个,它确实有效。

    //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://***.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 使用的示例。 Run Greasemonkey script on the same page, multiple times?

【讨论】:

以上是关于在执行代码技术之前等待页面加载的用户脚本?的主要内容,如果未能解决你的问题,请参考以下文章

在加载html之前运行脚本vuejs [重复]

JavaScript加载与执行

js怎样页面加载之前执行

高性能JavaScript学习笔记

编写高性能 JavaSCript 注意点

关于js的执行与加载