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

[油猴脚本开发指南]基本油猴编程的一些常见误区

[复制链接]

118

主题

906

帖子

529

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
529
发表于 2021-8-9 17:58:36 | 显示全部楼层 | 阅读模式

前言

由于很多人都提出了一些基础的问题,或者一些很常见的问题,我在此整理了一部分误区,方便大家查阅以及纠正

找不到元素的问题

一般可能有两个因素,一个是存在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

以这个小iframe拿捏,我们可以根据id来获取

document.querySelector('#g_iframe')

也可以感觉除了id和class的其他属性来获取,比如name='contentFrame'

像这种除了id和class的其他属性,可以加上中括号和等于来进行判断

document.querySelector('[name="contentFrame"]')

另外queryslector是按层级匹配,但是不需要把每层都写上,即使隔层也可以匹配到的

图片.png

这里我们想获取updn

document.querySelector('.g-btmbar .updn')

就可以了,不需要填写btmbar和updn中间那层元素

html中的标签也可以作为queryselect的搜索因素的

比如

图片.png

网页只有这一个iframe标签,那我们可以直接

document.querySelector('iframe')

来获取

如果存在大量的相同的class元素,并且没什么特征怎么办?

通过queryselectorall获取全部匹配的元素,然后通过for循环,根据每个元素的特征来判断是否是我们需要的元素,这个在百度去广告那节

我们有进行讨论过

https://bbs.tampermonkey.net.cn/thread-688-1-1.html

关于脚本作用域问题

一旦你开启了沙盒模式,你的沙盒中的函数与网页的函数是隔离的

如果你再写代码的过程中网页与沙盒之间产生了一些函数的交互或者调用,一定要注意这个问题

这时候可以考虑在沙盒对网页的元素进行监控或者挂载一些实践

关于这个问题在平时是不常见的,如果发生了这种问题,相信那时候的你是已经知道如何解决得了!

70

主题

682

帖子

596

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
596

猫咪币纪念章热心会员活跃会员突出贡献三好学生中秋纪念章

发表于 2021-8-9 18:09:15 | 显示全部楼层
代码无了?。。
上不慕古,下不肖俗。为疏为懒,不敢为狂。为拙为愚,不敢为恶。/ 微信公众号:一之哥哥
回复

使用道具 举报

3

主题

30

帖子

82

积分

注册会员

Rank: 2

积分
82
发表于 2021-8-9 18:17:32 | 显示全部楼层
const w = unsafeWndow || window,
    id = setInterval(() => {
        if (myvideojs = myvideojs || w.videoPlayer) {
            if (!!getmyvideo("html5player")) {
                clearInterval(id);
                // 该干嘛干嘛
            }
        }
    }, 500);

随手举个我之前百度云中等待window中的videojs加载完成的代码,

补充一下,油猴中要注意unsafeWindow和window是不一样的。。。。

注意调用的范围

回复

使用道具 举报

118

主题

906

帖子

529

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
529
发表于 2021-8-9 18:27:30 | 显示全部楼层
涛之雨 发表于 2021-8-9 18:17
[md]```js
const w = unsafeWndow || window,
    id = setInterval(() => {

呜呜呜,又见哥哥了!
回复

使用道具 举报

118

主题

906

帖子

529

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
529
发表于 2021-8-9 18:32:16 | 显示全部楼层
涛之雨 发表于 2021-8-9 18:17
[md]```js
const w = unsafeWndow || window,
    id = setInterval(() => {

哥哥那个文章真的很有技术含量
回复

使用道具 举报

3

主题

19

帖子

10

积分

新手上路

Rank: 1

积分
10

三好学生

发表于 2021-8-9 19:49:02 | 显示全部楼层
涛之雨 发表于 2021-8-9 18:17
[md]```js
const w = unsafeWndow || window,
    id = setInterval(() => {

看不懂呀,可恶
回复

使用道具 举报

5

主题

65

帖子

113

积分

注册会员

Rank: 2

积分
113

活跃会员热心会员突出贡献三好学生猫咪币纪念章中秋纪念章

发表于 2021-8-9 21:34:20 | 显示全部楼层
给一个我自己写的函数:
  1. function getElement(parent, selector, timeout = 0) {
  2.   return new Promise(resolve => {
  3.     let result = parent.querySelector(selector);
  4.     if (result) return resolve(result);
  5.     let timer;
  6.     const mutationObserver = window.MutationObserver || window.WebkitMutationObserver || window.MozMutationObserver;
  7.     if (mutationObserver) {
  8.       const observer = new mutationObserver(mutations => {
  9.         for (let mutation of mutations) {
  10.           for (let addedNode of mutation.addedNodes) {
  11.             if (addedNode instanceof Element) {
  12.               result = addedNode.matches(selector) ? addedNode : addedNode.querySelector(selector);
  13.               if (result) {
  14.                 observer.disconnect();
  15.                 timer && clearTimeout(timer);
  16.                 return resolve(result);
  17.               }
  18.             }
  19.           }
  20.         }
  21.       });
  22.       observer.observe(parent, {
  23.         childList: true,
  24.         subtree: true
  25.       });
  26.       if (timeout > 0) {
  27.         timer = setTimeout(() => {
  28.           observer.disconnect();
  29.           return resolve(null);
  30.         }, timeout);
  31.       }
  32.     } else {
  33.       const listener = e => {
  34.         if (e.target instanceof Element) {
  35.           result = e.target.matches(selector) ? e.target : e.target.querySelector(selector);
  36.           if (result) {
  37.             parent.removeEventListener('DOMNodeInserted', listener, true);
  38.             timer && clearTimeout(timer);
  39.             return resolve(result);
  40.           }
  41.         }
  42.       };
  43.       parent.addEventListener('DOMNodeInserted', listener, true);
  44.       if (timeout > 0) {
  45.         timer = setTimeout(() => {
  46.           parent.removeEventListener('DOMNodeInserted', listener, true);
  47.           return resolve(null);
  48.         }, timeout);
  49.       }
  50.     }
  51.   });
  52. }
复制代码
原理是首先用querySelector获取元素,获取不到时用MutationObserver监听插入节点,直到获取到所需元素,页面不支持MutationObserver时改用DOMNodeInserted实现,获取到元素后自动移除相关监听事件。代码看不懂也没关系,直接复制过去用就行了。第1个参数是父节点,第2个参数同querySelector,第3个参数是可选的超时时间(毫秒),设定最长监听时间以防止长时间等待,默认0不设超时。返回类型为Promise,需要用.then链式调用,或者async/await实现类似同步调用的效果,以下是调用示例(推荐后一种方式):
  1. function example1() {
  2.   getElement(document, '#test').then(element => {
  3.     //...
  4.   });
  5. }

  6. async function example2() {
  7.   const element = await getElement(document, '#test');
  8.   //...
  9. }
复制代码


已有2人评分好评 油猫币 理由
王一之 + 1 + 4 学习了!
李恒道 + 1 ggnb!

查看全部评分 总评分:好评 +2  油猫币 +4 

回复

使用道具 举报

5

主题

65

帖子

113

积分

注册会员

Rank: 2

积分
113

活跃会员热心会员突出贡献三好学生猫咪币纪念章中秋纪念章

发表于 2021-8-9 21:37:46 | 显示全部楼层
cxxjackie 发表于 2021-8-9 21:34
给一个我自己写的函数:
原理是首先用querySelector获取元素,获取不到时用MutationObserver监听插入节点, ...

好像matches有兼容性问题,如果你的浏览器不支持,在代码前加一句:
  1. Element.prototype.matches = Element.prototype.matches || Element.prototype.matchesSelector || Element.prototype.webkitMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.mozMatchesSelector;
复制代码
回复

使用道具 举报

118

主题

906

帖子

529

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
529
发表于 2021-8-9 21:55:32 | 显示全部楼层
cxxjackie 发表于 2021-8-9 21:34
给一个我自己写的函数:
原理是首先用querySelector获取元素,获取不到时用MutationObserver监听插入节点, ...

哥哥牛逼!这个函数执行效率测试怎么样,我之前调用addEventListener在频繁的页面会拖慢速度很多
回复

使用道具 举报

118

主题

906

帖子

529

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
529
发表于 2021-8-9 21:56:43 | 显示全部楼层
cxxjackie 发表于 2021-8-9 21:34
给一个我自己写的函数:
原理是首先用querySelector获取元素,获取不到时用MutationObserver监听插入节点, ...

代码还真干净,我次次写代码一坨浆糊,ggnb!
回复

使用道具 举报

发表回复

本版积分规则

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