本帖最后由 极品小猫 于 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