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

【油猴开发指南】脚本执行的时机?为什么只在控制台有效

[复制链接]
  • TA的每日心情

    2023-10-19 09:35
  • 签到天数: 66 天

    [LV.6]常住居民II

    6

    主题

    126

    回帖

    219

    积分

    高级工程师

    积分
    219

    油中2周年生态建设者

    发表于 2022-12-9 16:56:17 | 显示全部楼层 | 阅读模式

    本帖最后由 极品小猫 于 2022-12-14 14:51 编辑

    本帖最后由 极品小猫 于 2022-12-12 08:49 编辑

    本教程为小白向

    本教程不涉猎高深技术,但可能会有部分专业的名词,请参看 API 名词清单中的说明。小白可放心阅读,如果你已经掌握了小白向的操作,后续还有进阶的操作,提高执行效率。

    小白写脚本,常常会遇到一些脚本执行时机的问题!
    下面为新手以定时器作为思路解决问题,如果你已经掌握了定时器的用法,希望进阶学习,可以看文末最后推荐监视器监听DOM变动的进阶方法文章(难度比较高,需要多练习)。

    API名词清单

    选择器为什么只在控制台里有效(如iframe问题)

    【油猴脚本开发指南】基本油猴编程的一些常见误区
    小白入门的基本知识,在阅读后续的问题解决方法之前,强烈推荐先阅读上述文章,先对脚本执行时机有个及认识。

    window.onload 事件与 setTimeout 倒计时定时器方案

    掌握控制台的使用之后,往往是控制台里执行某些代码是有效的,放到脚本里运行就没效了。此类问题多数因为元素未加载时脚本执行导致的。
    报错如:

    Uncaught TypeError: Cannot set property 'value' of null

    例:https://bbs.tampermonkey.net.cn/thread-3839-1-1.html
    例2:https://bbs.tampermonkey.net.cn/thread-3844-1-1.html

    通常情况下,你所创建的脚本默认是运行于网页DOM加载成功后,DOM可以理解为网页用于显示内容的HTML代码传输完毕时。

    除了DOM加载成功后,还有 window.onload 事件,即网页中所有的内容加载完毕后。这些内容包括引入的JS、CSS、图片等所有资源文件,只有等待这些资源加载完毕后才会执行脚本,但是不包括通过 ajax 技术动态加载的资源。

    window.addEventListener("load", function(){
        alert("网页内容加载完毕")
    }
    );
    //监听网页元素加载完毕时,执行提示窗口 
    setTimeout(function(){
        alert("5秒后提示!")
    }, 5000);
    //定时执行器,5秒后执行代码

    在实际操作中,onload 事件的效果与 setTimeout 延时执行的效果并无太多区别,只是一个明确了时机,一个是固定的时机,这些方法在效率上往往不太理想。同时,有些你想操作的对象,需要一定的触发条件,例如不仅需要等待内容加载完毕,同时需要选择后或者点击后这两个条件,因此 onload 与 setTimeout 的无法解决这些需求。

    在上述的例2中,访问 https://static.qspfw.moe.gov.cn/user/#/user/login 这一地址时,可以明显的察觉到网页是有一个加载的阶段,这意味着 onload 与 setTimeout 都无法准确的去预估网页什么时候会加载完成。

    什么是 ajax 动态加载技术

    前文提到了 ajax 动态加载技术,由于该内容的知识点过于庞大复杂,此处仅针对脚本执行时为什么不存在元素说明其工作机制。
    即你脚本想要对某个input文本框进修内容的读写,但是这个文本框并不是在你打开网页时就会显示,而是在你网页加载完成后,再单独进行局部内容加载的技术。常见于某些下载链接,在你点击某个按钮之后,网页不刷新就显示出来了。
    由于是后加载技术,导致你的脚本执行过早,获取不到操作的对象。

    setInterval 定时循环执行计时器

    例3:https://bbs.tampermonkey.net.cn/home.php?mod=spacecp
    在例3中,存在一个更复杂的情况,省市区街道的下拉框,需要前一级内容选择后才会显示,通过下面的这两行代码可以触发一次选择并显示下一级内容。

    document.querySelector('#resideprovince').value="北京市"; 
    document.querySelector('#resideprovince').onchange()

    这个内容是通过Ajax动态加载的,当触发了change事件后,整个省市街道的根节点的内容都会重新加载一遍,因此你无法简单的通过对触发 change 事件后来联动修改后续的信息,因为你不知道什么时候 ajax 动态加载才会完成。

    利用 setInterval 定时循环执行器是新手入门中最容易掌握的操作办法,只要掌握好定时循环的节奏即可。

    document.querySelector("#resideprovince").value = "北京市";
    document.querySelector("#resideprovince").onchange();
    let t = setInterval(function () {
      //设定循环定时器,1000毫秒=1秒,1秒钟检查一次目标对象是否出现
      let obj = document.querySelector("#residecity"); //声明要查询的对象
      if (obj) {
        //判断对象是否存在,存在则开始设置值
        obj.value = "朝阳区"; //对这个对象设置有效的值
        obj.onchange(); //触发 change 事件
        clearInterval(t); //清除循环定时器
      }
    }, 1000);

    通过这个循环定时器,可以更加高效的去检查对象目标出现时,执行某个代码,后续的街道等信息,可以以此类推进行套娃。
    此方法也可以适用于例2中案例

    封装循环定时检测函数

    function checkObj(selection, fn) {
      let t = setInterval(function () {
        //设定循环定时器,1000毫秒=1秒,1秒钟检查一次目标对象是否出现
        let obj = document.querySelector(selection); //声明要查询的对象
        if (obj) {
          //判断对象是否存在,存在则开始设置值
          fn(obj);
          clearInterval(t); //清除循环定时器
        }
      }, 1000);
    }
    //执行函数
    checkObj("#residecity", function (obj) {
      //传入选择器的参数,要目标对象出现时,要执行的操作函数,obj 为选择器的操作对象
      obj.value = "朝阳区"; //对这个对象设置有效的值
      obj.onchange(); //触发 change 事件
    });

    【进阶】addEventListener 监听器监听 DOMNodeInserted(DOM节点改变事件)

    https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener
    该方法是一个建议弃用的方法,但是小白入门之前首先需要学习解决死循环问题,这在新手操作中极容易出现的问题。也极有可能是你百度的时候,会学习到的方法。
    下述文章旨在掌握解决死循环问题,不是学习 DOMNodeInserted 的真正用途。
    如果你想学习DOM 节点的深度操作(例如需要处理ajax加载后的内容),我更推荐学习使用 MutationObserver 观察器。

    【油猴脚本开发指南】addEventListener DOMNodeInserted监听元素变动
    [油猴脚本开发指南]脚本往页面上添加新元素

    【高级进阶 】MutationObserver 观察器

    API:https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver
    [油猴脚本开发指南]MutationObserver简单详解
    https://bbs.tampermonkey.net.cn/thread-1007-1-1.html
    [油猴脚本开发指南]MutationObserver简易例子
    https://bbs.tampermonkey.net.cn/thread-1008-1-1.html
    [油猴脚本开发指南]MutationObserver实战
    https://bbs.tampermonkey.net.cn/thread-1017-1-1.html

    已有2人评分好评 油猫币 贡献 理由
    大药科技 + 1 + 1 很给力!
    王一之 + 1 + 4 + 1 感谢分享

    查看全部评分 总评分:好评 +2  油猫币 +5  贡献 +1 

    该用户从未签到

    2

    主题

    7

    回帖

    12

    积分

    助理工程师

    积分
    12
    发表于 2022-12-9 19:04:16 | 显示全部楼层
    谢谢大佬的解答。
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2022-3-8 11:41
  • 签到天数: 2 天

    [LV.1]初来乍到

    22

    主题

    862

    回帖

    1361

    积分

    荣誉开发者

    积分
    1361

    荣誉开发者卓越贡献油中2周年生态建设者油中3周年挑战者 lv2

    发表于 2022-12-9 20:42:59 | 显示全部楼层
    我做那个库很大一部分原因也是因为这些问题太日经了,很多小白往往不关心背后的原理,只想要一个解决方案,那我就直接把最好的方案拍他脸上
    回复

    使用道具 举报

    该用户从未签到

    2

    主题

    7

    回帖

    12

    积分

    助理工程师

    积分
    12
    发表于 2022-12-9 22:42:34 | 显示全部楼层
    大佬你好,我是你文章中的例一,我的代码刷新网页后控制台直接输入也无效,必须在当前网页ctrl+shift+c随意查看一个元素,然后再输入代码才能成功,这是什么原因呢?
    回复

    使用道具 举报

  • TA的每日心情
    无聊
    2023-11-2 17:37
  • 签到天数: 275 天

    [LV.8]以坛为家I

    113

    主题

    450

    回帖

    969

    积分

    荣誉开发者

    积分
    969

    荣誉开发者油中2周年卓越贡献生态建设者

    发表于 2022-12-9 22:50:49 | 显示全部楼层
    期待gg的更新
    I frequently record, because want to leave something.
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2023-2-28 23:59
  • 签到天数: 191 天

    [LV.7]常住居民III

    637

    主题

    5189

    回帖

    6073

    积分

    管理员

    非物质文化遗产社会摇传承人

    积分
    6073

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

    发表于 2022-12-10 14:07:59 | 显示全部楼层
    lc1550458025 发表于 2022-12-9 22:42
    大佬你好,我是你文章中的例一,我的代码刷新网页后控制台直接输入也无效,必须在当前网页ctrl+shift+c随意 ...

    iframe问题
    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道

    入驻了爱发电https://afdian.net/a/lihengdao666
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。
    回复

    使用道具 举报

    该用户从未签到

    2

    主题

    7

    回帖

    12

    积分

    助理工程师

    积分
    12
    发表于 2022-12-10 19:37:37 | 显示全部楼层

    谢谢大佬,已经解决。
    回复

    使用道具 举报

  • TA的每日心情

    2023-10-19 09:35
  • 签到天数: 66 天

    [LV.6]常住居民II

    6

    主题

    126

    回帖

    219

    积分

    高级工程师

    积分
    219

    油中2周年生态建设者

    发表于 2022-12-12 08:15:08 | 显示全部楼层
    cxxjackie 发表于 2022-12-9 20:42
    我做那个库很大一部分原因也是因为这些问题太日经了,很多小白往往不关心背后的原理,只想要一个解决方案, ...

    我觉得那个库的入手门槛好像还是有点高
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2022-3-8 11:41
  • 签到天数: 2 天

    [LV.1]初来乍到

    22

    主题

    862

    回帖

    1361

    积分

    荣誉开发者

    积分
    1361

    荣誉开发者卓越贡献油中2周年生态建设者油中3周年挑战者 lv2

    发表于 2022-12-12 21:10:18 | 显示全部楼层
    极品小猫 发表于 2022-12-12 08:15
    我觉得那个库的入手门槛好像还是有点高

    应该是get方法的Promise比较费解,我是为了await才写成Promise,结果多数人只能看懂.then的用法,早知道跟each一样做成回调函数了。我发现很多人对Promise都望而却步,明明这是个很好的特性,论坛这方面的教程还是不太够啊(我给道哥说过让他补充一下,他好像忘了哈哈)。
    回复

    使用道具 举报

  • TA的每日心情

    2023-10-19 09:35
  • 签到天数: 66 天

    [LV.6]常住居民II

    6

    主题

    126

    回帖

    219

    积分

    高级工程师

    积分
    219

    油中2周年生态建设者

    发表于 2022-12-13 09:18:29 | 显示全部楼层
    cxxjackie 发表于 2022-12-12 21:10
    应该是get方法的Promise比较费解,我是为了await才写成Promise,结果多数人只能看懂.then的用法,早知道 ...

    promise 就和 MutationObserver 一样,setInterval 循环去判断一个对象是否存在是一个很小白的办法

    即便小白有一定基础了,让它使用 MutationObserver  简直就要命了,这已经不是一个等级了,不是小白看3天教程就能入门的东西

    小白使用这个库,不仅要能理解async/await的使用关系,还得懂得基础的promise,太难了

    C大的那个库,不是入门级小白能掌握的东西
    回复

    使用道具 举报

    发表回复

    本版积分规则

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