[油猴脚本开发指南]基本油猴编程的一些常见误区
# 前言由于很多人都提出了一些基础的问题,或者一些很常见的问题,我在此整理了一部分误区,方便大家查阅以及纠正
# 找不到元素的问题
一般可能有两个因素,一个是存在ifame,另外一个问题可能是页面加载的问题
## 页面加载与元素的那点破事
页面加载不等于元素已经出现,我们通常在f12控制台调试和查看的是现有的页面,并不代表页面加载完毕后就一定具备这个元素
尽管很多页面会在加载的时候就出现所有元素
但是也有很多页面,会在渲染完毕后,再根据xhr请求数据等方式,再在页面上绘制新的数据,这个时候如果我们在页面加载完毕和获取数据绘制之间进行获取元素
是获取不到的。
解决方法也很简单,
这里我提供两个常见的方法
1.使用Setinterval来进行循环判断,当获取元素不为空的时候继续执行,但需要注意不要创建过多的定时器,以及不使用的时候可以考虑销毁定时器
https://www.runoob.com/jsref/met-win-setinterval.html
2.通过DOM插入监控来判断
在bilibili的例子中我们已经搞过了,通过上层已经存在的元素对其进行dom监听,然后再插入的时候会触发这个函数,来进行一些操作
但是需要注意把握好下层元素的数量问题,如果绘制元素过多反复触发,会极大的延迟运行速度
```
let ops=document.querySelector('#arc_toolbar_report .ops');
//插入三连之后好像会重新生成,不添加就不会重新生成,暂时没弄清什么情况,先这样处理了.
//主要作用是监听ops的DOMNodeInserted事件,等它修改完成之后再插入我们的三连按钮,另外注意run-at是document-end,要等待ops生成之后再监听,不然query返回null会报错
//这个事件会多次调用,但是我们insertBefore插入如果元素存在,只是修改而不会新增
ops.addEventListener("DOMNodeInserted", function(event) {
let share=document.querySelector('.share');
share.parentElement.insertBefore(triple,share);
});
```
## iframe的那点破事
可以参考https://music.163.com/#
我们可以获取到iframe元素后通过conetentWindow进入iframe的作用域来执行相应的函数
document.querySelector('#g_iframe').contentWindow.document.querySelector
在我的印象里好像是contentWindow内的document通常同域下才可以使用,而非同域是没有办法的
如果没法调用contentWindow下的document我们也有其他办法的
相信你一定想到了樱花动漫那一节课吧?
通过match匹配让脚本运行在iframe内就好了!
https://bbs.tampermonkey.net.cn/thread-274-1-1.html
# queryselect的临时大培训!
很多人都说document.queryselect的时候经常不知道怎么找元素
我这里列几个例子
![图片.png](data/attachment/forum/202108/09/181917ce6eccp9o93ecw91.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "图片.png")
以这个小iframe拿捏,我们可以根据id来获取
document.querySelector('#g_iframe')
也可以感觉除了id和class的其他属性来获取,比如name='contentFrame'
像这种除了id和class的其他属性,可以加上中括号和等于来进行判断
document.querySelector('')
另外queryslector是按层级匹配,但是不需要把每层都写上,即使隔层也可以匹配到的
![图片.png](data/attachment/forum/202108/09/182234papulfir7b7mm22m.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "图片.png")
这里我们想获取updn
document.querySelector('.g-btmbar .updn')
就可以了,不需要填写btmbar和updn中间那层元素
html中的标签也可以作为queryselect的搜索因素的
比如
![图片.png](data/attachment/forum/202108/09/182410ufsrrmx36x6g2z76.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "图片.png")
网页只有这一个iframe标签,那我们可以直接
document.querySelector('iframe')
来获取
如果存在大量的相同的class元素,并且没什么特征怎么办?
通过queryselectorall获取全部匹配的元素,然后通过for循环,根据每个元素的特征来判断是否是我们需要的元素,这个在百度去广告那节
我们有进行讨论过
https://bbs.tampermonkey.net.cn/thread-688-1-1.html
# 关于脚本作用域问题
一旦你开启了沙盒模式,你的沙盒中的函数与网页的函数是隔离的
如果你再写代码的过程中网页与沙盒之间产生了一些函数的交互或者调用,一定要注意这个问题
这时候可以考虑在沙盒对网页的元素进行监控或者挂载一些实践
关于这个问题在平时是不常见的,如果发生了这种问题,相信那时候的你是已经知道如何解决得了!
代码无了?。。 ```js
const w = unsafeWndow || window,
id = setInterval(() => {
if (myvideojs = myvideojs || w.videoPlayer) {
if (!!getmyvideo("html5player")) {
clearInterval(id);
// 该干嘛干嘛
}
}
}, 500);
```
随手举个我之前百度云中等待window中的videojs加载完成的代码,
补充一下,油猴中要注意unsafeWindow和window是不一样的。。。。
注意调用的范围
涛之雨 发表于 2021-8-9 18:17
```js
const w = unsafeWndow || window,
id = setInterval(() => {
呜呜呜,又见哥哥了! 涛之雨 发表于 2021-8-9 18:17
```js
const w = unsafeWndow || window,
id = setInterval(() => {
哥哥那个文章真的很有技术含量 涛之雨 发表于 2021-8-9 18:17
```js
const w = unsafeWndow || window,
id = setInterval(() => {
看不懂呀,可恶 给一个我自己写的函数:
function getElement(parent, selector, timeout = 0) {
return new Promise(resolve => {
let result = parent.querySelector(selector);
if (result) return resolve(result);
let timer;
const mutationObserver = window.MutationObserver || window.WebkitMutationObserver || window.MozMutationObserver;
if (mutationObserver) {
const observer = new mutationObserver(mutations => {
for (let mutation of mutations) {
for (let addedNode of mutation.addedNodes) {
if (addedNode instanceof Element) {
result = addedNode.matches(selector) ? addedNode : addedNode.querySelector(selector);
if (result) {
observer.disconnect();
timer && clearTimeout(timer);
return resolve(result);
}
}
}
}
});
observer.observe(parent, {
childList: true,
subtree: true
});
if (timeout > 0) {
timer = setTimeout(() => {
observer.disconnect();
return resolve(null);
}, timeout);
}
} else {
const listener = e => {
if (e.target instanceof Element) {
result = e.target.matches(selector) ? e.target : e.target.querySelector(selector);
if (result) {
parent.removeEventListener('DOMNodeInserted', listener, true);
timer && clearTimeout(timer);
return resolve(result);
}
}
};
parent.addEventListener('DOMNodeInserted', listener, true);
if (timeout > 0) {
timer = setTimeout(() => {
parent.removeEventListener('DOMNodeInserted', listener, true);
return resolve(null);
}, timeout);
}
}
});
}原理是首先用querySelector获取元素,获取不到时用MutationObserver监听插入节点,直到获取到所需元素,页面不支持MutationObserver时改用DOMNodeInserted实现,获取到元素后自动移除相关监听事件。代码看不懂也没关系,直接复制过去用就行了。第1个参数是父节点,第2个参数同querySelector,第3个参数是可选的超时时间(毫秒),设定最长监听时间以防止长时间等待,默认0不设超时。返回类型为Promise,需要用.then链式调用,或者async/await实现类似同步调用的效果,以下是调用示例(推荐后一种方式):
function example1() {
getElement(document, '#test').then(element => {
//...
});
}
async function example2() {
const element = await getElement(document, '#test');
//...
}
cxxjackie 发表于 2021-8-9 21:34
给一个我自己写的函数:
原理是首先用querySelector获取元素,获取不到时用MutationObserver监听插入节点, ...
好像matches有兼容性问题,如果你的浏览器不支持,在代码前加一句:Element.prototype.matches = Element.prototype.matches || Element.prototype.matchesSelector || Element.prototype.webkitMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.mozMatchesSelector; cxxjackie 发表于 2021-8-9 21:34
给一个我自己写的函数:
原理是首先用querySelector获取元素,获取不到时用MutationObserver监听插入节点, ...
哥哥牛逼!这个函数执行效率测试怎么样,我之前调用addEventListener在频繁的页面会拖慢速度很多 cxxjackie 发表于 2021-8-9 21:34
给一个我自己写的函数:
原理是首先用querySelector获取元素,获取不到时用MutationObserver监听插入节点, ...
{:4_105:}代码还真干净,我次次写代码一坨浆糊,ggnb!