【油猴开发指南】脚本执行的时机?为什么只在控制台有效
本帖最后由 极品小猫 于 2022-12-14 14:51 编辑> 本帖最后由 极品小猫 于 2022-12-12 08:49 编辑
# 本教程为小白向
本教程不涉猎高深技术,但可能会有部分专业的名词,请参看 API 名词清单中的说明。小白可放心阅读,如果你已经掌握了小白向的操作,后续还有进阶的操作,提高执行效率。
小白写脚本,常常会遇到一些脚本执行时机的问题!
下面为新手以定时器作为思路解决问题,如果你已经掌握了定时器的用法,希望进阶学习,可以看文末最后推荐监视器监听DOM变动的进阶方法文章(难度比较高,需要多练习)。
#API名词清单
* (https://developer.mozilla.org/zh-CN/docs/Web/API/Window/load_event)
* (https://developer.mozilla.org/zh-CN/docs/Web/API/setTimeout)
* (https://developer.mozilla.org/zh-CN/docs/Web/API/setInterval)
* (https://developer.mozilla.org/zh-CN/docs/Web/API/clearInterval)
* (https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/iframe)
*
## 选择器为什么只在控制台里有效(如iframe问题)
[【油猴脚本开发指南】基本油猴编程的一些常见误区](https://bbs.tampermonkey.net.cn/thread-835-1-1.html)
小白入门的基本知识,在阅读后续的问题解决方法之前,**强烈推荐**先阅读上述文章,先对脚本执行时机有个及认识。
## window.onload 事件与 setTimeout 倒计时定时器方案
掌握控制台的使用之后,往往是控制台里执行某些代码是有效的,放到脚本里运行就没效了。此类问题多数因为元素未加载时脚本执行导致的。
报错如:
> Uncaught TypeError: Cannot set property 'value' of null
例:[https://bbs.tampermonkey.net.cn/thread-3839-1-1.html](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 技术动态加载的资源。
```javascript
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](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](https://bbs.tampermonkey.net.cn/home.php?mod=spacecp)
在例3中,存在一个更复杂的情况,省市区街道的下拉框,需要前一级内容选择后才会显示,通过下面的这两行代码可以触发一次选择并显示下一级内容。
```javascript
document.querySelector('#resideprovince').value="北京市";
document.querySelector('#resideprovince').onchange()
```
这个内容是通过Ajax动态加载的,当触发了change事件后,整个省市街道的根节点的内容都会重新加载一遍,因此你无法简单的通过对触发 change 事件后来联动修改后续的信息,因为你不知道什么时候 ajax 动态加载才会完成。
利用 setInterval 定时循环执行器是新手入门中最容易掌握的操作办法,只要掌握好定时循环的节奏即可。
```javascript
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中案例
# 封装循环定时检测函数
```javascript
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监听元素变动](https://bbs.tampermonkey.net.cn/thread-3864-1-1.html)
[\[油猴脚本开发指南\]脚本往页面上添加新元素](https://bbs.tampermonkey.net.cn/thread-237-1-1.html)
# 【高级进阶 】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 谢谢大佬的解答。 我做那个库很大一部分原因也是因为这些问题太日经了,很多小白往往不关心背后的原理,只想要一个解决方案,那我就直接把最好的方案拍他脸上{:4_108:} 大佬你好,我是你文章中的例一,我的代码刷新网页后控制台直接输入也无效,必须在当前网页ctrl+shift+c随意查看一个元素,然后再输入代码才能成功,这是什么原因呢? 期待gg的更新 lc1550458025 发表于 2022-12-9 22:42
大佬你好,我是你文章中的例一,我的代码刷新网页后控制台直接输入也无效,必须在当前网页ctrl+shift+c随意 ...
iframe问题 李恒道 发表于 2022-12-10 14:07
iframe问题
谢谢大佬,已经解决。 cxxjackie 发表于 2022-12-9 20:42
我做那个库很大一部分原因也是因为这些问题太日经了,很多小白往往不关心背后的原理,只想要一个解决方案, ...
我觉得那个库的入手门槛好像还是有点高 极品小猫 发表于 2022-12-12 08:15
我觉得那个库的入手门槛好像还是有点高
应该是get方法的Promise比较费解,我是为了await才写成Promise,结果多数人只能看懂.then的用法,早知道跟each一样做成回调函数了。我发现很多人对Promise都望而却步,明明这是个很好的特性,论坛这方面的教程还是不太够啊(我给道哥说过让他补充一下,他好像忘了哈哈)。 cxxjackie 发表于 2022-12-12 21:10
应该是get方法的Promise比较费解,我是为了await才写成Promise,结果多数人只能看懂.then的用法,早知道 ...
promise 就和 MutationObserver 一样,setInterval 循环去判断一个对象是否存在是一个很小白的办法
即便小白有一定基础了,让它使用 MutationObserver简直就要命了,这已经不是一个等级了,不是小白看3天教程就能入门的东西
小白使用这个库,不仅要能理解async/await的使用关系,还得懂得基础的promise,太难了
C大的那个库,不是入门级小白能掌握的东西{:4_93:}
页:
[1]
2