上一主题 下一主题
ScriptCat,新一代的脚本管理器脚本站,与全世界分享你的用户脚本油猴脚本开发指南教程目录
返回列表 发新帖

[油猴脚本开发指南] 元素等待器的简易方法

[复制链接]
  • TA的每日心情
    难过
    2024-4-24 18:57
  • 签到天数: 13 天

    [LV.3]偶尔看看II

    24

    主题

    31

    回帖

    281

    积分

    荣誉开发者

    积分
    281

    荣誉开发者油中2周年生态建设者喜迎中秋

    发表于 2024-7-12 16:47:55 | 显示全部楼层 | 阅读模式

    元素等待器的简易方法

    前言

    元素等待器是什么? 现在网页基本都是基于框架去编写的, 那么 Dom 元素就并非直接写在 HTML 中的, 而是通过 JS 渲染出来的. 那么就会出现一个问题, 在 load / DOMContentLoaded 事件触发时, 网页上并非是完全的 HTML 结构, 还有一些元素仍然在异步载入中. 所以现在的脚本如果需要在页面载入时操作 Dom , 那么就需要通过一个元素等待器去等待元素载入后, 再进行后续的操作.

    MutationObserver 是一个很好的解决方案, 但是有一个问题是, 写起来确实有点繁琐. 如果不是直接导入函数库/已经写好的函数, 那么手写的话会非常的麻烦. 只写一遍还好说, 但是如果每次都要写的话会很麻烦.

    有时候想写一些简单的脚本, 不想开一个新的项目, 想直接在网页编辑器中编写代码, 又不想依赖第三方库的时候, 想要获取异步载入的元素怎么办呢? 最简单的方法: 轮询.

    第三库的异步元素等待器: ElementGetter .

    MutationObserve 知识

    粗暴延时

    // 函数入口
    const main = () => {
        const dom = document.querySelector( '#app' );
    
        // any code
    };
    
    // 粗暴延时5s开始运行函数
    setTimeout( main, 5_000 );

    轮询

    轮询 本质上是对于粗暴延时的简单优化, 因为延时不仅反应慢, 并且也有可能等待后元素仍然没有载入导致函数错误.

    // 函数入口
    const main = () => {
        // any code
    };
    
    // 每 500 ms 查询一次元素载入状态
    const getterTimer = setInterval( () => {
        const dom = document.querySelector( '#app' );
        // 如果元素载入
        if ( dom ) {
            // 清除定时器
            clearInterval( getterTimer );
            // 运行函数
            main();
        }
    }, 500 );

    这样, 只需要一次定时器的开启和关闭操作, 就可以完成等待异步元素载入的操作. 在一些随便写写的简单脚本上, 会比手写 MutationObserver 会方便不少.

    当然, 如果开一个项目写脚本, 能用 MutationObserver 还是优先选用 MutationObserver 去载入函数.

    简单封装轮询

    虽然 js/ts 主流的类型语法库还没有支持, 但是 Promise.withResolvers() 是已经在所有浏览器全面支持的, 是可以直接使用的.

    /**
     * 获取异步载入的元素
     *
     * @parma {string} selector 元素选择器
     * @parma {number} [delayPerMs = 500] 轮询延时
     *
     * @Return {Promise<Element>} 载入的元素
     * */
    function elementGetter( selector, delayPerMs = 500 ) {
        let { promise, resolve, reject } = Promise.withResolvers();
    
        // 轮询获取元素载入情况
        const getterTimer = setInterval( () => {
            const dom = document.querySelector( '#app' );
            // 如果元素载入
            if ( dom ) {
                // 清除定时器
                clearInterval( getterTimer );
    
                // 兑现 promise
                resolve( dom );
            }
        }, delayPerMs );
    
        // 返回 Promise
        return promise;
    }
    
  • TA的每日心情
    无聊
    2024-8-24 22:31
  • 签到天数: 4 天

    [LV.2]偶尔看看I

    1

    主题

    2

    回帖

    9

    积分

    助理工程师

    积分
    9
    发表于 2024-7-12 23:20:30 | 显示全部楼层
    那复杂方法是什么方案了?
    回复

    使用道具 举报

  • TA的每日心情
    难过
    2024-4-24 18:57
  • 签到天数: 13 天

    [LV.3]偶尔看看II

    24

    主题

    31

    回帖

    281

    积分

    荣誉开发者

    积分
    281

    荣誉开发者油中2周年生态建设者喜迎中秋

    发表于 2024-7-13 01:04:09 | 显示全部楼层

    s11s 发表于 2024-7-12 23:20

    那复杂方法是什么方案了?

    可以看 MutationObserve 知识 这个链接里的内容. 也就是我说的 MutationObserver , 然后这里面有一个 延迟查询代码范例解读 章节, 这个章节里分析了一个函数, 就是一个利用 MutationObserver 实现元素等待器的函数.

    回复

    使用道具 举报

  • TA的每日心情
    开心
    前天 13:37
  • 签到天数: 213 天

    [LV.7]常住居民III

    305

    主题

    4196

    回帖

    4061

    积分

    管理员

    积分
    4061

    管理员荣誉开发者油中2周年生态建设者喜迎中秋油中3周年挑战者 lv2

    发表于 2024-7-14 17:18:41 | 显示全部楼层
    《暴力美学》
    上不慕古,下不肖俗。为疏为懒,不敢为狂。为拙为愚,不敢为恶。
    回复

    使用道具 举报

    发表回复

    本版积分规则

    快速回复 返回顶部 返回列表