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

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

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

    前天 16:48
  • 签到天数: 62 天

    [LV.6]常住居民II

    6

    主题

    129

    帖子

    213

    积分

    高级工程师

    Rank: 6Rank: 6

    积分
    213

    油中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

    已有1人评分好评 油猫币 贡献 理由
    王一之 + 1 + 4 + 1 感谢分享

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

    该用户从未签到

    2

    主题

    9

    帖子

    11

    积分

    助理工程师

    Rank: 1

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

    使用道具 举报

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

    [LV.1]初来乍到

    21

    主题

    624

    帖子

    1080

    积分

    荣誉开发者

    Rank: 10Rank: 10Rank: 10

    积分
    1080

    卓越贡献活跃会员热心会员突出贡献三好学生荣誉开发者喜迎中秋油中2周年

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

    使用道具 举报

    该用户从未签到

    2

    主题

    9

    帖子

    11

    积分

    助理工程师

    Rank: 1

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

    使用道具 举报

  • TA的每日心情
    无聊
    1 小时前
  • 签到天数: 82 天

    [LV.6]常住居民II

    72

    主题

    386

    帖子

    541

    积分

    荣誉开发者

    Rank: 10Rank: 10Rank: 10

    积分
    541

    荣誉开发者国庆纪念章油中2周年

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

    使用道具 举报

  • TA的每日心情
    开心
    昨天 23:59
  • 签到天数: 170 天

    [LV.7]常住居民III

    470

    主题

    4281

    帖子

    4322

    积分

    管理员

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

    Rank: 10Rank: 10Rank: 10

    积分
    4322

    喜迎中秋国庆纪念章荣誉开发者管理员油中2周年

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

    iframe问题
    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。
    回复

    使用道具 举报

    该用户从未签到

    2

    主题

    9

    帖子

    11

    积分

    助理工程师

    Rank: 1

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

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

    使用道具 举报

  • TA的每日心情

    前天 16:48
  • 签到天数: 62 天

    [LV.6]常住居民II

    6

    主题

    129

    帖子

    213

    积分

    高级工程师

    Rank: 6Rank: 6

    积分
    213

    油中2周年三好学生

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

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

    使用道具 举报

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

    [LV.1]初来乍到

    21

    主题

    624

    帖子

    1080

    积分

    荣誉开发者

    Rank: 10Rank: 10Rank: 10

    积分
    1080

    卓越贡献活跃会员热心会员突出贡献三好学生荣誉开发者喜迎中秋油中2周年

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

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

    使用道具 举报

  • TA的每日心情

    前天 16:48
  • 签到天数: 62 天

    [LV.6]常住居民II

    6

    主题

    129

    帖子

    213

    积分

    高级工程师

    Rank: 6Rank: 6

    积分
    213

    油中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大的那个库,不是入门级小白能掌握的东西
    回复

    使用道具 举报

    发表回复

    本版积分规则

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