油猴脚本不求人!零基础油猴脚本编写指南

9973 2026-07-14 12:23:46

前言

本文首发于我的博客和其乐论坛,未经授权禁止转载

本文全程使用原生JavaScript进行编程,不使用像jQuery这样的第三方库

视频链接

零基础油猴脚本编写指南【上】 (对应0x01~0x05)

0x00 前置知识

本节简单的介绍一下一些名词的概念,了解为主,直接跳过也是可以的

HTML、CSS和JS

HTML即超文本标记语言,它用来描述各种网页元素,类似与一份清单,浏览器读取以后根据它来加载对应的资源

CSS即层叠样式表,它只用来描述网页元素的外观,还可以添加少许动画,但是不影响功能

JS即JavaScript,它是一种网页脚本语言,用来让静态的网页能够响应各种动作,还可以挖矿

如果把HTML比作木偶,那么CSS就是木偶的衣服,JS就是木偶的提线,用来操控木偶

油猴和浏览器

当你访问链接的时候,浏览器会先下载链接对应的文件(通常是一个HTML文件),然后根据HTML描述的对象列表,来下载图片、视频等资源,有的CSS和JS内嵌在HTML文件里,有的则是单独的文件,等必要的资源加载完成以后,浏览器才能生成可以交互的网页

油猴实际上是一个加载器,它把用户脚本安插在最开始获取到的HTML文件中,这样浏览器加载网页的时候,也会加载特定的用户脚本,这样我们就能够通过编写用户脚本来修改网页了

HTML5和DOM

HTML5是现行的HTML标准,DOM即文档对象模型,是一用对象表示HTML标签的方法,通过DOM,我们可以简单的修改文档对象的属性,来轻松修改HTML标签的内容

0x01 初识开发者工具

工欲善其事必先利其器,本节简单的介绍一下开发者工具的使用。

本节以我的博客作为演示,地址:chrxw.com

个人比较喜欢Firefox的开发者工具,以下介绍均使用Firefox进行演示,如果使用Chrome的话可能布局会有细微差异,不过功能是类似的。

Chrome自带的开发者工具貌似没有汉化,建议使用别的Chromium内核的浏览器(比如新版Edge),功能都差不多。

在浏览器中按F12即可打开开发者工具

查看器,左侧可以查看页面的HTML内容,右侧显示的是选中的DOM元素的CSS

控制台,可以运行自己编写的JS代码。

也可以在设置里打开“分离式控制台”,这样就可以在所有TAB里都显示控制台了。

调试器,可以用来单步调试JS

网络,可以查看浏览器的网络请求

储存,在这个TAB中可以查看网站在本地保存的内容,最常用的是Cookie,其次是LocalStorge(本地储存),还有个由浏览器自动管理的Session(会话储存),JS可以访问的只有Cookie和LocalStorge。

0x02 初识DOM

本节通过使用开发者工具,简单的演示一下如何操作DOM元素,以我的博客作为演示,地址:chrxw.com

首先打开开发者工具,然后激活选择器,然后点击“标题”,这样在右边的查看器中就会显示我们点击的对象

右键高亮的元素,然后点击“在控制台使用”(如果是Chromium或者Chrome,点击“复制”/“Copy”->“复制JS路径”/“Copy JS path”,然后把复制结果粘贴到控制台中)

Firefox自动创建了一个temp0变量,按一下回车,可以看到控制台输出了一个DOM元素,点一下左边的箭头展开,可以看到这个对象的各种属性。

接下来我们尝试修改一下它的内容

temp0.textContent = 'hello world';

按回车执行,然后可以看到,网页的标题已经被修改了

接着我们修改一下CSS,让它居中显示

temp0.style.textAlign='center';

接下来我们创建一个新的DOM元素,然后插入到网页中

let btn = document.createElement('button');

btn.textContent = '按钮';

btn.addEventListener('click',() => {

alert('hello world');

});

temp0.appendChild(btn);

点一下新出现的按钮,可以看到消息弹窗

代码讲解:

PS: document 也是DOM元素,它代表整个网页,其他DOM元素都是它的子集。

let btn = document.createElement('button');

btn.textContent = '按钮';

这两行代码创建了一个button对象,它内部的文本为按钮,由浏览器自动生成的HTML表达式为

btn.addEventListener('click',() => {

alert('hello world');

});

这三行代码为btn对象添加了事件监听器,addEventListener的第一个参数是事件监听器的名称,第二个参数是接收到指定事件后调用的函数,()=>{ ... }是匿名函数的缩略写法,匿名函数还有另一种等效写法function(){ ... },或者写成普通函数,也是可以的。

//匿名函数写法2

btn.addEventListener('click',function(){

alert('hello world');

});

//普通函数的写法

btn.addEventListener('click',foo);//foo后面不加()

function foo(){

alert('hello world');

}

添加好事件监听器以后,点一下按钮,就会触发click事件,然后由浏览器调用我们绑定的方法alert('hello world');,弹出消息框

temp0.appendChild(btn);

A.appendChild(B)的作用是把B元素插入到A元素的子元素的末尾,也就是把btn对象插到temp0中,由浏览器自动帮我们修改了HTML文本

//修改前

Chr_小屋

//修改后

Chr_小屋

//这是我们新创建的DOM元素

通过操作DOM元素的属性,就可以很方便的修改网页的显示效果,添加各种功能。

0x03 DOM进阶之获取DOM元素

通过修改DOM元素的属性,可以很方便地修改网页,但是首先我们得获取我们想要修改的DOM元素

本节将会介绍如何获通过代码获取现存的DOM元素,以我的博客作为演示,地址:about.html

1. 使用ID进行获取

WEB开发者设计网页的时候,会给一些经常需要修改的元素添加id属性,原则上每个元素的id都是不同的,所以如果某个元素具有id属性,我们就可以使用getElementById来获取

需要注意的是,如果元素没有id属性的话这个方法就行不通了

随便打开一篇博文,最下面的评论框是带有id属性的,使用开发者工具我们可以看到,它的id为comment

我们可以用document.getElementById( id名称 )的方式获取DOM元素

let box = document.getElementById('comment');

box.value = '通过ID获取DOM';

代码非常好理解,先调用getElementById方法获取DOM,然后修改它的value属性,效果就是文本框的内容被修改了

PS: textContent 属性修改的是HTML文本,而 value 属性修改的是文本框中的内容,不影响HTML文本

2. 使用CSS选择器进行获取

在一个网页中,具有id属性的元素不会太多,大多数时候都是没有这个属性的,这时候可以根据元素的class属性,使用querySelector(获取符合条件的第一个元素)或者querySelectorAll(获取符合条件的全部元素数组)进行获取

本节只介绍常用的方法,更详细的介绍可以参考这个:CSS 选择器参考手册

根据class进行选择

标题元素的class为entry-title m-n font-thin text-black l-h,class选择器的语法为.+class名称,如果有多个class名称,则用.分隔

所以标题元素的选择器写法为.entry-title.m-n.font-thin.text-black.l-h

在CSS选择器语法中,空格代表子元素的意思,所以不能随意添加空格。

通过搜索,我们发现entry-title在HTML文档只出现了一次,因此选择器也可以简化写作.entry-title,代码如下

//两种写法等效

document.querySelector('.entry-title.m-n.font-thin.text-black.l-h');

document.querySelector('.entry-title');

在实际运用中,没必要给出完整的class,只需要写独一无二的部分即可

根据id进行选择(可以用,但还是建议用getElementById)

id选择器的语法为#+id名称,评论框的id是comment,所以选择器写法为#comment,代码如下

//两种写法等效,推荐第一种

document.getElementById('comment');

document.querySelector('#comment');

根据元素类型选择

还可以根据DOM元素的类型进行选择,如果网页中只有一个特定种类的元素,那么直接写元素类型即可

使用开发者工具,我们可以知道,评论框的类型为textarea,而且整个网页只有一个textarea,因此选择器可以直接写成textarea,代码如下

document.querySelector('textarea');

根据相对关系进行选择

有的时候,想要操作的元素可能没有独一无二的属性,那么就可以根据相对关系进行选择

A>B表示在A的一级子元素中查找B

A B表示在A的内部查找B(查找范围不仅包括子元素,还包括孙元素等)

A+B表示与A相邻的B元素(平级关系,非父子关系)

这里的 A 和 B 代表的是选择器,选择器可以嵌套使用。

A.B,不表示相对关系,它表示类型是A,class名是B的元素,这个要注意。

还是用标题元素举例子

先找到容易定位的元素,然后再根据相对关系寻找想要找的元素,方法有很多,下面举几个例子

//以下语句都是等效的

document.querySelector('main>div>div>header>h1');//一级子元素

document.querySelector('main header>h1');//所有子元素

document.querySelector('main h1');//这样也可以

document.querySelector('header>h1');//多个方法定位header对象

document.querySelector('#small_widgets>h1');

document.querySelector('.bg-light.lter.wrapper-md>h1');

document.querySelector('header.bg-light>h1');

写成这个样子主要是为了举例子,实际使用中选择器越简洁越好

根据其他属性名称进行选择

[xxx]选择带有xxx属性的元素

[xxx=sss]选择xxx属性等于sss的元素

[xxx~=sss]选择xxx属性包含sss的元素(sss必须是整个单词)

[xxx|=sss]选择xxx属性以sss开头的元素(sss必须是整个单词)

[xxx^=sss]选择xxx属性以sss开头的元素(sss可以是单词的一部分)

[xxx$=sss]选择xxx属性以sss结尾的元素(sss可以是单词的一部分)

[xxx*=sss]选择xxx属性包含sss的元素(sss可以是单词的一部分)

通过开发者工具可以看出高亮的DOM元素具有for属性,属性值为comment,代码如下,感受一下几种用法的区别

document.querySelector('[for=comment]');//完整匹配

document.querySelector('[for|=co]');//匹配开头,匹配失败,for属性中没有co这个单词

document.querySelector('[for~=co]');//匹配任意位置,匹配失败,for属性中没有co这个单词

document.querySelector('[for^=co]');//匹配开头

document.querySelector('[for*=co]');//匹配任意位置

document.querySelector('[for*=mm]');//匹配任意位置

document.querySelector('[for$=nt]');//匹配结尾

组合选择器

上面介绍过的选择器可以组合使用,比如选择表情按钮中的矢量图片

代码如下,自己体会一下各个部分是什么意思,以及哪些部分是没有必要的

//两种写法效果一样

document.querySelector('textarea.form-control[name=text]+div.OwO>.OwO-logo svg');

document.querySelector('.OwO-logo svg');

0x04 DOM进阶之修改DOM元素

拿到了DOM对象,接下来就可以进行各种操♂作了

本节将会介绍DOM元素的常用方法,以我的博客作为演示,地址:about.html

以标题元素为例,首先我们先获取DOM对象

let h1 = document.querySelector('h1');

console.log(h1);

使用开发者工具可以非常方便的看到DOM元素的方法和属性

枚举子元素

DOM.children

返回值为DOM元素的所有子元素数组,如果不含有子元素,返回的是空数组。

可以通过DOM.children[i]的方式获取某一个子元素

获取/修改文本内容

DOM.textContent

返回值为当前DOM元素内的显示字符(没有被 < > 包起来的所有字符),不仅包括自身,还包括所有子元素

DOM.innerText也有类似的效果,区别是它会去掉多余的换行符

也可以修改DOM.textContent和`DOM.innerText

不过要注意的是,这两个方法会直接替换掉元素内部的内容,如果元素内部有其他子元素的话,子元素会消失。

获取/修改元素CSS

只能修改DOM元素的内联CSS,全局CSS得修改Style元素才行。

CSS相关内容不做过多介绍,贴个简单的教程:菜鸟CSS教程

DOM.style,获取DOM元素的内联CSS样式

DOM.style.[css名称],获取DOM元素的特定CSS属性,例如textAlign

DOM.style.cssText,获取DOM元素的内联CSS样式,纯文本格式

同样的,也可以用类似的方法修改CSS样式

DOM.style.[css名称] = 'xxx',修改DOM元素的特定CSS属性为xxx

DOM.style.cssText = 'aaa: xxx;',直接修改DOM元素的内联CSS文本,可以一次设置多个CSS

如果想隐藏某些元素的话,可以把它的CSS的display属性设置成none,就会被隐藏起来了

新增子元素

DOM.appendChild( [要插入的DOM对象] )

appendChild会将新插入的对象放在所有子元素的末尾

DOM.insertBefore( [要插入的DOM对象] , [DOM对象,将要插在这个对象之前])

insertBefore可以指定新插入的对象的位置

let h1 = document.querySelector('h1');

let b1 = document.createElement('button');

b1.textContent='按钮1';

let b2 = document.createElement('button');

b2.textContent='按钮2';

h1.appendChild(b1);

h1.insertBefore(b2,h1.children[0]);

删除对象

DOM.remove()

这是个方法,所以要加(),调用后会在HTML文本中删除对象对应的内容

添加/删除事件监听器

DOM.addEventListener( [事件名称] , [回调函数])添加事件监听器

DOM.removeEventListener( [事件名称] , [回调函数])移除事件监听器

也可以用以前的写法(不推荐):

DOM.on[事件名称] = [回调函数]添加事件监听器

DOM.on[事件名称] = null移除事件监听器

比如要让h1对象响应click事件,代码如下

let h1 = document.querySelector('h1');

//比较推荐这种写法

h1.addEventListener('click',() => {

alert('响应click事件');

});

//也可以用旧风格的写法

h1.onclick = () => {

alert('响应click事件');

};

//一些解释:

//() => { ... } 定义了一个匿名函数,相当于 function(){ ... }

//也可以使用实名函数,比如定义了function foo(){ ... }以后,也可以这么绑定:

//h1.addEventListener('click',foo);

//h1.onclick = foo;

删除绑定的方法也是类似的,不再赘述

模拟鼠标点击

DOM.click()

相当于鼠标点了一下DOM对象,触发它的click事件(如果有的话)

0x05 第一个油猴脚本

讲了这么久,都是在介绍前端的基础知识,接下来开始正文

首先让我们新建一个用户脚本

按Ctrl+S即可保存

元信息

元信息是给油猴插件使用的,用来记录脚本的属性

新建脚本时给出的默认元信息如下:

// ==UserScript==

// @name New Userscript //名称

// @namespace http://tampermonkey.net/ //命名空间

// 说明:@namespace相同,@name也相同的脚本会被油猴当做是同一个脚本,这两个属性起到区分不同脚本的作用

// @version 0.1 //版本号

// @description try to take over the world! //说明

// @author You //作者

// @match http://*/* //在什么网址下启用该脚本

// @grant none //特殊权限(调用GM_开头的函数需要先申请权限)

// ==/UserScript==

还有部分比较常见的元信息:

// @include /https://example.com/.*/ //和@match差不多,但是支持正则表达式,我更喜欢用这个

// @connect example.com //如果脚本需要访问跨域资源,需要提前申明

// @license AGPL-3.0 //脚本的授权许可信息

// @icon https://blog.chrxw.com/favicon.ico //脚本的图标(显示在脚本列表里)

代码区域

(function() {

'use strict';

// Your code here...

})();

首先定义了一个匿名函数function() { ... },也可以写成我常用的格式,都是等效的:

(() => {

'use strict';

// Your code here...

})();

用()把匿名函数包起来,再在后面加一个(),表示直接执行这个匿名函数

其实完全可以把代码写到匿名函数的外部,都可以执行,只是定义域不同而已

'use strict';是伪代码,它告诉解释器,开启严格模式

开启严格模式以后,会进行更严格的语法检查,建议开启,有利于养成良好的编码习惯

// 非严格模式,允许访问未定义变量

(() => {

y = 3.14; // 不报错

})();

// 严格模式,允许访问已定义变量

(() => {

"use strict"; // 启用严格模式

let y = 3.14; // 不报错(y已定义)

})();

// 严格模式,不允许访问未定义变量

(() => {

"use strict"; // 启用严格模式

y = 3.14; // 报错(y未定义)

})();

实现功能

接下来我们为脚本编写一些功能,以我的博客作为演示,地址:about.html

首先我们需要修改元信息,让脚步只在特定的网页生效

然后添加作者名称和脚本名称(除了@match都可以随便写)

// ==UserScript==

// @name 第一个油猴脚本

// @namespace http://blog.chrxw.com/

// @version 0.1

// @description hello world

// @author Chr_

// @match https://blog.chrxw.com/about.html

// @grant none

// ==/UserScript==

我们先不编写脚本,我们先使用开发者工具测试一下代码

首先我们找一个对象下手(如图)

它没有id属性,但是它的class名称独一无二,所以我们可以直接用document.querySelector('.item-thumb')获取到DOM元素

let a = document.querySelector('.item-thumb');

修改元素的背景图

a.style.backgroundImage='url(/usr/customize/logo.png)';

添加点击事件

a.addEventListener('click',() => {

a.style.background='#f0f';

});

点一下以后背景变成了品红色

最后我们编写油猴脚本,让这一切自动完成

// ==UserScript==

// @name 第一个油猴脚本

// @namespace http://blog.chrxw.com/

// @version 0.1

// @description hello world

// @author Chr_

// @match https://blog.chrxw.com/about.html

// @grant none

// ==/UserScript==

(() => {

'use strict';

let a = document.querySelector('.item-thumb');

a.style.backgroundImage='url(/usr/customize/logo.png)';

a.addEventListener('click',() => {

a.style.background='#f0f';

});

// Your code here...

})();

保存脚本以后,刷新页面,可以看到元素的背景图片已经改掉了,说明脚本运行成功了。

0x06 脚本实战,Auto_Sub3

脚本获取链接:Auto_Sub3

脚本功能

安装脚本以后,首页可以打开一个面板,主要功能是一键-3

元信息

// ==UserScript==

// @name Auto_Sub3

// @namespace https://blog.chrxw.com

// @version 2.1

// @description 一键快乐-3

// @author Chr_

// @include https://keylol.com/*

// @license AGPL-3.0

// @icon https://blog.chrxw.com/favicon.ico

// @grant GM_setValue

// @grant GM_getValue

// @grant GM_xmlhttpRequest

// ==/UserScript==

元信息提供了这个脚本的基本信息,我们还可以知道,它申请了GM_setValue,GM_getValue和GM_xmlhttpRequest三个特殊权限

代码总览

// 上次-3时间

let VLast = 0;

// 今天还能不能-3

let VCan3 = true;

// 自动展开

let VShow = false;

// 自动-3

let VAuto = false;

// 音效载入

let sound = new Audio("https://blog.chrxw.com/usr/keylol/gas.mp3");

(function () { // 每次加载以后从这里运行

'use strict';

loadCFG();

addBtns();

if (VShow) {

switchPanel();

}

if (VCan3 && VAuto) {

autoRoll();

}

})();

// 添加GUI

function addBtns() { ... }

// 显示小轮盘

function showLiteRoll() { ... }

// 自动打开面板

function fBtnShow() { ... }

// 自动-3

function fBtnAuto() { ... }

// 显示布尔

function bool2txt(bool) { ... }

// 隐藏面板

function switchPanel() { ... }

// 读取设置

function loadCFG() { ... }

// 保存设置

function saveCFG() { ... }

// 检查能否-3

function checkZP() { ... }

// 禁止-3

function disableS3() { ... }

// 自动-3

function autoRoll() { ... }

先不用太纠结代码,建议先用调试模式看一下代码流程

脚本调试模式

在设置里可以打开调试模式,开启以后在加载完脚本以后会自动暂停,可以用来动态调试脚本

打开调试模式以后,先打开开发者工具,然后刷新页面,可以看到暂停在debugger语句上了

给我们感兴趣的函数添加断点,然后点“继续运行(F8)”

可以自己尝试跟踪一下每个函数的运行过程,不再赘述

代码详解

脚本入口

// 上次-3时间

let VLast = 0;

// 今天还能不能-3

let VCan3 = true;

// 自动展开

let VShow = false;

// 自动-3

let VAuto = false;

// 音效载入

let sound = new Audio("https://blog.chrxw.com/usr/keylol/gas.mp3");

(function () {

'use strict';

loadCFG();

addBtns();

if (VShow) {

switchPanel();

}

if (VCan3 && VAuto) {

autoRoll();

}

})();

首先定义了5个全局变量,然后运行了匿名函数,在这个匿名函数里完成了脚本的初始化工作

在匿名函数内部调用了这么几个函数:loadCFG,addBtns,switchPanel,autoRoll

loadCFG/saveCFG - 读写配置

// 读取设置

function loadCFG() {

let t = null;

t = GM_getValue('VLast');

t = Number(t);

if (t != t) { t = 0; }

VLast = t;

t = GM_getValue('VCan3');

VCan3 = Boolean(t);

t = GM_getValue('VShow');

VShow = Boolean(t);

t = GM_getValue('VAuto');

VAuto = Boolean(t);

// 日期变更,重置VCan3

let d = new Date();

let day = d.getDate();

let hour = d.getHours();

if (day != VLast && hour >= 8) {

VCan3 = true;

VLast = day;

}

saveCFG();

}

// 保存设置

function saveCFG() {

GM_setValue('VLast', VLast);

GM_setValue('VCan3', VCan3);

GM_setValue('VShow', VShow);

GM_setValue('VAuto', VAuto);

}

脚本保存数据的方法有很多种,可以利用原生的Cookie,也可以利用HTML5标准中的LocalStorge,但是这两个地方只能保存文本,我们还需要自行完成类型转换,非常不方便,所以我用的是油猴自带的储存空间

// 需要在元信息里提前申明,才可以使用

// @grant GM_setValue

// @grant GM_getValue

GM_setValue( 键名 , 键值),保存某个值

GM_getValue( 键名 ),如果键名不存在,返回null

可以在脚本编辑器的储存TAB看到当前脚本的储存空间

saveCFG非常简单,就是把4个全局变量保存起来

loadCFG也很简单,就是从储存空间读取4个变量,然后获取现在的时间和日期,判断日期有没有变更,如果日期变了,并且当前小时数大于8,就把Vcan3赋值成true,然后把VLast改成今天的日期,最后调用saveCFG保存4个全局变量

addBtns - 添加GUI

// 添加GUI

function addBtns() {

function genButton(text, foo, id) {

let b = document.createElement('button');//创建类型为button的DOM对象

b.textContent = text; //修改内部文本为text

b.style.cssText = 'margin: 3px 5px;' //添加样式(margin可以让元素间隔开一定距离)

b.addEventListener('click', foo); //绑定click的事件的监听器

if (id) { b.id = id; } //如果传入了id,就修改DOM对象的id

return b; //返回修改好的DOM对象

}

function genDiv(cls) {

let d = document.createElement('div'); //创建类型为div的DOM对象

if (cls) { d.className = cls }; //如果传入了cls,就修改DOM对象的class

return d; //返回修改好的DOM对象

}

function genPanel(name, visiable) {

let p = genDiv(name); //创建类型为div,class为name的DOM对象

p.id = name; //修改DOM对象的id为name

p.style.cssText = 'width: 100%;height: 100%;';//修改DOM对象的样式(占满父容器)

if (!visiable) { p.style.display = 'none'; }//修改DOM对象的样式(不可见)

return p; //返回修改好的DOM对象

}

//使用CSS选择器获取对象

let btnSwitch = document.querySelector('.index_left_title>div');

if (btnSwitch == null) { return; }

btnSwitch.id = 'btnSwitch1';

btnSwitch.title = '点这里开启/隐藏控制面板';

btnSwitch.style.cssText = 'width: auto;padding: 0 5px;cursor: pointer;';

btnSwitch.addEventListener('click', switchPanel);

let panelArea = document.querySelector('.index_navi_left');

let panelOri = document.querySelector('.index_left_detail');

panelOri.id = 'panelOri'

let panel54 = genPanel('panel54');

let aLP = document.createElement('a');

aLP.href = 'https://keylol.com/t571093-1-1';

let img54 = document.createElement('img');

img54.src = 'https://gitee.com/chr_a1/gm_scripts/raw/master/index.png';

img54.alt = '总之这里是54的名言';

img54.style.cssText = 'float: right;margin-top: -28px;height: 100%;'

aLP.appendChild(img54);

let btnArea = genDiv('btnArea');

btnArea.style.cssText = 'width: 210px;text-align: center;margin-top: -10px;';

let btnS3 = genButton('【一键-3】', autoRoll, 'btnS3');

if (!VCan3) {

btnS3.style.textDecoration = 'line-through';

btnS3.textContent = '今天已经不能-3了';

}

let btnShow = genButton(bool2txt(VShow) + '自动打开面板', fBtnShow, 'btnShow');

let btnAuto = genButton(bool2txt(VAuto) + '自动每日-3', fBtnAuto, 'btnAuto');

btnArea.appendChild(btnS3);

btnArea.appendChild(btnShow);

btnArea.appendChild(btnAuto);

panel54.appendChild(aLP);

panel54.appendChild(btnArea);

panelArea.insertBefore(panel54, panelArea.children[1]);

}

看着很长,实际上很简单,首先定义了3和内部函数genButton,genDiv,genPanel,用来生成我们需要的DOM元素

然后尝试用选择器获取DOM对象

let btnSwitch = document.querySelector('.index_left_title>div');//使用CSS选择器获取DOM对象

if (btnSwitch == null) { return; } //如果页面中不存在这个对象,就直接返回,结束运行

btnSwitch.id = 'btnSwitch1'; //添加id属性

btnSwitch.title = '点这里开启/隐藏控制面板'; //添加title属性(鼠标悬停的时候显示的文本)

btnSwitch.style.cssText = 'width: auto;padding: 0 5px;cursor: pointer;';//添加CSS样式

btnSwitch.addEventListener('click', switchPanel);//绑定click事件的回调函数为switchPanel

可以利用开发者工具看一下是哪个对象

剩下的部分也很好理解,创建3个button对象,1个img对象,依次添加到名为panel54的div对象里,最后再把panel54添加到panelArea中

let panelArea = document.querySelector('.index_navi_left');

let panelOri = document.querySelector('.index_left_detail');

panelOri.id = 'panelOri'

let panel54 = genPanel('panel54');

let aLP = document.createElement('a');

aLP.href = 'https://keylol.com/t571093-1-1';

let img54 = document.createElement('img');

img54.src = 'https://gitee.com/chr_a1/gm_scripts/raw/master/index.png';

img54.alt = '总之这里是54的名言';

img54.style.cssText = 'float: right;margin-top: -28px;height: 100%;'

aLP.appendChild(img54);

let btnArea = genDiv('btnArea');

btnArea.style.cssText = 'width: 210px;text-align: center;margin-top: -10px;';

let btnS3 = genButton('【一键-3】', autoRoll, 'btnS3');

if (!VCan3) {

btnS3.style.textDecoration = 'line-through';

btnS3.textContent = '今天已经不能-3了';

}

let btnShow = genButton(bool2txt(VShow) + '自动打开面板', fBtnShow, 'btnShow');

let btnAuto = genButton(bool2txt(VAuto) + '自动每日-3', fBtnAuto, 'btnAuto');

btnArea.appendChild(btnS3);

btnArea.appendChild(btnShow);

btnArea.appendChild(btnAuto);

panel54.appendChild(aLP);

panel54.appendChild(btnArea);

panelArea.insertBefore(panel54, panelArea.children[1]);

通过switchPanel,可以切换显示原来的元素和脚本的面板

// 隐藏面板

function switchPanel() {

let panel1 = document.getElementById('panel54');

let panel2 = document.getElementById('panelOri');

if (panel1.style.display == 'none') {

btnSwitch1.textContent = '今天你【-3】了吗 - By Chr_';

panel1.style.display = 'block';

panel2.style.display = 'none';

} else {

btnSwitch1.textContent = '关注重点';

panel1.style.display = 'none';

panel2.style.display = 'block';

}

}

运行效果:

autoRoll - 自动-3

最后我们看一下核心功能,自动-3

// 自动-3

function autoRoll() {

try {

sound.play();

} catch (e) {

console.error(e);

}

let v = 0;

gethash();

function gethash() {

GM_xmlhttpRequest({

method: "GET",

url: 'https://keylol.com/plugin.php?id=steamcn_lottery:view&lottery_id=43',

onload: function (response) {

if (response.status == 200) {

let m = response.responseText.match(/plugin\.php\?id=steamcn_lottery:view&lottery_id=43&hash=(.+)&roll/);

let hash = m ? m[1] : null;

console.log(hash);

if (hash != null) {

roll(hash);

roll(hash);

roll(hash);

} else {

disableS3();

saveCFG();

}

} else {

console.error('出错');

}

}

});

}

function roll(hash) {

GM_xmlhttpRequest({

method: "GET",

url: 'https://keylol.com/plugin.php?id=steamcn_lottery:view&lottery_id=43&hash=' + hash + '&roll',

onload: function (response) {

if (response.status == 200) {

console.log(response.responseText);

} else {

console.error('出错')

}

if (++v == 3) {

disableS3();

saveCFG();

}

}

});

}

}

乍一看非常复杂,实际上也很好理解

try {

sound.play();

} catch (e) {

console.error(e);

}

let v = 0;

gethash();

首先调用sound.play(),播放蒸汽泄漏的音效,sound是全局变量,会在网页载入的时候自动加载音频资源,如果遇到错误就忽略错误(比如网络问题加载音频失败的话就会报错)

然后调用了gethash方法

function gethash() {

GM_xmlhttpRequest({

method: "GET",

url: 'https://keylol.com/plugin.php?id=steamcn_lottery:view&lottery_id=43',

onload: function (response) {

if (response.status == 200) {

let m = response.responseText.match(/plugin\.php\?id=steamcn_lottery:view&lottery_id=43&hash=(.+)&roll/);

let hash = m ? m[1] : null;

console.log(hash);

if (hash != null) {

roll(hash);

roll(hash);

roll(hash);

} else {

disableS3();

saveCFG();

}

} else {

console.error('出错');

}

}

});

}

这个函数使用了GM_xmlhttpRequest,这个东西是一个网络请求器,深入了解可以参考官方文档(英语),GM_xmlhttpRequest类似于xmlHttpRequest,两者用法几乎一样,只是GM_xmlhttpRequest不存在跨域的问题

// 需要先在元信息里申明,才可以使用

// @grant GM_xmlhttpRequest

简单的用法如下

GM_xmlhttpRequest({

method: '', //请求方法,可以是 GET POST HEAD 等

url: '', //请求路径,简单的说就是url

onload: (response) => {

//请求被响应时执行,response是接受到的响应的对象

//response.status,获取响应代码(200代表请求成功,4xx和5xx代表请求出错)

//response.responseText,获取响应文本,本例中获取到的是HTML文本

//response.responseXML,获取XML格式的响应文本

}

});

看到onload的回调函数部分

onload: (response) => {

if (response.status == 200) {

let m = response.responseText.match(/plugin\.php\?id=steamcn_lottery:view&lottery_id=43&hash=(.+)&roll/);

let hash = m ? m[1] : null;

console.log(hash);

if (hash != null) {

roll(hash);

roll(hash);

roll(hash);

} else {

disableS3();

saveCFG();

}

} else {

console.error('出错');

}

}

首先判断状态码是不是200(代表请求成功),请求成功才会处理响应的内容

可以在浏览器中打开链接,用开发者工具模拟一下处理过程

我们把响应的内容当做纯文本处理,使用正则表达式提取想要的内容

正则表达式深入学习可以参考这个:正则表达式 - 语法

let a = document.body.innerHTML;

console.log(a.match(/plugin\.php\?id=steamcn_lottery:view&lottery_id=43&hash=(.+)&roll/));

简单的讲一下上面的正则表达式是什么意思

/plugin\.php\?id=steamcn_lottery:view&lottery_id=43&hash=(.+)&roll/

// 两边的 / 就是告诉解释器里面是正则表达式,没别的意思

// 按照不同元素拆分,如下:

// plugin - 匹配“plugin”这个字符串

// \. - 匹配“.”,“.”原来有别的意思,在前面加“\”转义以后就只有字面意思了

// id=steamcn_lottery:view&lottery_id=43&hash= - 匹配一整串字符串

// (.+) - “()”是子匹配的意思,“.+”意思是匹配至少一个字符

// - “(.+)”的意思是不仅要匹配至少一个字符,而且匹配到的文本需要返回

// &roll - 匹配“&roll”这个字符串

简单点说就是先找到plugin.php?id=steamcn_lottery:view&lottery_id=43&hash=[XXXXXXX]&roll这一串字符串,然后提取中间的XXXXXXX

执行效果如下,可以看到返回了一个数组,第一个元素是匹配到的字符串,第二个元素是子匹配项,我们只需要子匹配项,也就是8b51a2d3(这个值不同用户是不一样的,所以需要动态获取)

然后就是赋值操作,用到了三目运算符号

let hash = m ? m[1] : null;

// D = A ? B : C

// 等价于:

if(A){

D = B;

}else{

D = C;

}

// 当A的逻辑意义为true时,返回值为B

// 当A的逻辑意义为false时,返回值为C

// 类似这样

let a = true ? 1 : 2; // a = 1

let a = false ? 1 : 2; // a = 2

如果正则表达式没有匹配到合适的字符串,返回的内容是null,逻辑意义等于false,这时候hash的值就取null

如果正则表达式匹配到了合适的字符串,返回的内容是数组,逻辑意义等于true,这时候hash的值就取数组的第2个元素

然后根据hash决定是不是要继续运行

if (hash != null) {

roll(hash);

roll(hash);

roll(hash);

} else {

disableS3();

saveCFG();

}

最后是roll函数,核心也是一个网络请求器

function roll(hash) {

GM_xmlhttpRequest({

method: "GET",

url: 'https://keylol.com/plugin.php?id=steamcn_lottery:view&lottery_id=43&hash=' + hash + '&roll',

onload: function (response) {

if (response.status == 200) {

console.log(response.responseText);

} else {

console.error('出错')

}

if (++v == 3) {

disableS3();

saveCFG();

}

}

});

}

实际上就是访问https://keylol.com/plugin.php?id=steamcn_lottery:view&lottery_id=43&hash=[XXXXXXX]&roll这个网站,然后把响应结果打印出来

可以根据上一步得到的hash在浏览器中试一下

调用以后,会自增v,如果v = 3,就调用disableS3和saveCFG,修改了按钮的名称,然后保存全局变量

本文链接:https://blog.chrxw.com/archives/2021/02/08/1449.html

转载请保留本文链接,谢谢

【2025年8月】 最新桌面CPU天梯图排名、综合性能对比、平均帧、AI性能以及...
三点更新:看台上那位中国球迷,你无须哭泣