V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Tsccai
V2EX  ›  jQuery

油猴脚本能拦截$(document).ready 吗?

  •  
  •   Tsccai · 2023-09-13 16:30:14 +08:00 · 3013 次点击
    这是一个创建于 462 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近在研究一个古老的系统,想写一个油猴脚本。我发现页面中有这样的代码:

    $(document).ready(
      closeIt();
    )
    

    程序会在 dom 加载完后检查浏览器版本,不满足直接关闭页面窗口。 那么,油猴脚本能否拦截 ready 函数,不让其执行原来的程序呢?

    22 条回复    2024-04-09 13:50:54 +08:00
    kkk9
        1
    kkk9  
       2023-09-13 18:25:08 +08:00
    实践出真知!

    ```javascript
    (function() {
    'use strict';
    $(document).ready(function() {
    // ... 你自己的代码
    }
    })
    ```
    kkk9
        2
    kkk9  
       2023-09-13 18:25:50 +08:00
    漏了一个) 记得自己补足
    ysc3839
        3
    ysc3839  
       2023-09-13 18:31:52 +08:00 via Android
    拦截 ready 肯定行呀,替换掉“$”,然后调用原函数,判断一下传入的参数是不是 document ,或者返回的对象有没有 ready 函数,有的话替换掉 ready 。
    不过个人感觉从检查浏览器版本那下手会更好吧?建议能给原网页。
    caomu
        4
    caomu  
       2023-09-13 19:11:32 +08:00 via Android
    如果不是直接载入脚本,而是用浏览器扩展载入的话,主流的 gm 、tm 、vm 扩展应该都是支持设置脚本生效时间,默认是页面载入完成后,也可以手工改成载入开始时。
    vvhy
        5
    vvhy  
       2023-09-13 19:33:56 +08:00
    用//@run-at document-start ,还不够快的话去设置开启即时注入
    Tsccai
        6
    Tsccai  
    OP
       2023-09-13 23:39:40 +08:00
    @ysc3839 呃,因为网页其他代码还需要用 jQuery ,所以直接干掉$是不可取的。我是准备在原本的 closeIt 函数加载后,ready 执行前,用油猴注入一个新的 closeIt 函数,函数里面啥也不干。但这样干有一个小瑕疵,就是不能保证每次都成功。
    ysc3839
        7
    ysc3839  
       2023-09-13 23:51:36 +08:00 via Android
    @Tsccai 并不是直接干掉,而是“然后调用原函数”,所以并没有问题。
    ysc3839
        8
    ysc3839  
       2023-09-13 23:52:58 +08:00 via Android
    @Tsccai 如果 closeIt 是全局的话,似乎可以用 getter 和 setter 来阻止覆盖。
    另外还是建议给出原网站,以便判断最佳方案。
    Tsccai
        9
    Tsccai  
    OP
       2023-09-13 23:54:49 +08:00
    @kkk9 我这里测试结果是原网页的 ready 回调函数先执行,然后油猴注入的代码后执行。我只想让原页面中的 ready 回调函数不要执行
    Tsccai
        10
    Tsccai  
    OP
       2023-09-13 23:59:11 +08:00
    @ysc3839 原网页位于单位内网,不方便展示。不过 closeIt 这个函数确实在 window 对象下,我其实通过覆盖一个同名函数的方式也能大致实现,就是不大稳定
    Pipecraft
        11
    Pipecraft  
       2023-09-14 00:17:35 +08:00
    @Tsccai #10 如果 closeIt 是全局的,那就直接覆盖 closeIt ,不用管 $.ready 了。
    为了保证在 ready 之前执行,添加
    // @run-at document-start
    并监听 readystatechange 事件或使用 MutationObserver 来检查 closeIt 是否已加载并在 ready 执行期覆盖它。

    完整代码: https://pastebin.mozilla.org/CbxsMXbz
    Pipecraft
        12
    Pipecraft  
       2023-09-14 00:20:23 +08:00
    @Pipecraft #11 "并在 ready 执行期覆盖它" => "并在 ready 执行前覆盖它"
    Tsccai
        13
    Tsccai  
    OP
       2023-09-14 00:28:25 +08:00
    @Pipecraft 感觉这个方案不错,回头我试试看,感谢!
    Puteulanus
        14
    Puteulanus  
       2023-09-14 00:34:00 +08:00
    我问 ChatGPT 它这么说的:
    $(document).ready() 内部使用了原生 JavaScript 的 document.addEventListener("DOMContentLoaded", callback) 或者 Internet Explorer 的 document.attachEvent("onreadystatechange", callback)。

    那在 @run-at document-start 的时候直接把 document 上的这两个函数劫持掉试试
    openmynet
        15
    openmynet  
       2023-09-14 00:34:54 +08:00
    ```js

    // @run-at document-start

    Object.defineProperty(window, "$", {
    get: () => {
    return (selector) => {
    const node = window._$(selector);
    if (node && selector instanceof Document) {
    node.ready = (callback) => {
    console.log("$ has been replaced");
    if (typeof callback == "function") {
    callback();
    }
    };
    }
    return node;
    };
    },
    set: (fn) => {
    window._$ = fn;
    },
    });

    ```
    openmynet
        16
    openmynet  
       2023-09-14 00:39:34 +08:00
    chnwillliu
        17
    chnwillliu  
       2023-09-14 08:08:44 +08:00 via Android
    为什么要覆盖这些?覆盖 $.fn.ready 不就行了?

    看来 jQuery 是真老了,没人知道了。

    $.fn.ready = function (){

    }
    Marlon
        18
    Marlon  
       2023-09-14 08:25:10 +08:00 via iPhone
    @vvhy 感谢,最近恰好遇到一个要阻止自动执行函数,试试提前注入,重写方法。
    Tsccai
        19
    Tsccai  
    OP
       2023-09-27 18:21:00 +08:00
    过了半个月终于是有空在单位的电脑上试一下了。
    首先,ready 这个函数还被其他 jQuery 的插件和脚本使用,所以 @chnwillliu 直接覆盖为一个空函数的方案并不可行。
    不过,我们可以在覆盖的这个函数内部对传入的回调函数`cb`进行过滤,检测到`cb.toString().indexOf('closeIt')>-1`时直接返回,否则则执行该回调函数即可。也不再需要去覆盖 closet()函数本身了。
    golangggg
        20
    golangggg  
       254 天前
    @vvhy 大佬, 请问您说的设置开启即时注入 这个在哪里, 我没搜到资料,
    我目前的问题就是设置了 @run-at document-start 后 偶尔好用, 偶尔不好用,
    目标站, 在代码第一行就执行了 alert(); 我想抵消掉这个 alert
    vvhy
        21
    vvhy  
       254 天前
    @golangggg #20 我用的是 tampermonkey ,设置里配置模式选择高级
    golangggg
        22
    golangggg  
       253 天前
    @vvhy 非常感谢, 昨晚我自己摸索了一下 确实在高级里有一个注入模式 ,改成及时之后真的很有效
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3473 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 04:46 · PVG 12:46 · LAX 20:46 · JFK 23:46
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.