偶尔会写一些 TamperMonkey 的脚本, 但每次间隔时间久了之后, 就经常忘记要怎么写了, 所以在这里记录一下一些常用的代码段, 有需要的时候可以直接拿来使用。

使用 insertAdjacentHTML 添加 HTML 代码

1
2
3
4
5
6
7
8
9
10
11
// 添加样式或其他HTML元素
let style = `<style>
.sidebar {
display: none !important;
}
.content{
margin: 0 !important;
}
</style>`;

document.head.insertAdjacentHTML('beforeend', style);

insertAdjacentHTML() 方法将指定的文本解析为 Element 元素,并将结果节点插入到 DOM 树中的指定位置

  • 'beforebegin':元素自身的前面。

  • 'afterbegin':插入元素内部的第一个子节点之前。

  • 'beforeend':插入元素内部的最后一个子节点之后。

  • 'afterend':元素自身的后面。

添加外部样式文件

1
2
3
4
5
6
7
8
9
10
11
12
13

/**
* 添加其他网站上的外部样式文件
* @param {*} href 外部样式文件的url
* @returns
*/
function addLinkCss(href) {
let link = document.createElement('link');
link.type = 'text/css';
link.rel = 'stylesheet';
link.href = href;
document.head.appendChild(link);
}

添加样式使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// 自定义的样式(为防止样式冲突, 建议每个样式名称取专有的前缀, 例如此处的 acme-)
let style = `
.acme-button{
position:fixed;
bottom: 20px;
right: 20px;
color: red;
padding: 15px;
}

.acme-speed{
display: none;
}

.acme-button:hover{
color: green;
width: 300px;
height: 200px;
}

.acme-button:hover .acme-speed{
display:inline;
}

.acme-shadow {
box-shadow: 0 2px 12px 2px rgba(0, 0, 0, 0.1);
border-radius: 8px;
border: 1px solid #ebeef5;
background-color: #fff;
color: #303133;
transition: 0.3s;
}`;

document.head.insertAdjacentHTML('beforeend', style);

let htmL =`
<div class="acme-button acme-shadow">
<span>show</span>
<select name="speedx" class="acme-speed">
<option value="16">16.0x</option>
<option value="12">12.0x</option>
<option value="8">8.0x</option>
<option value="6">6.0x</option>
<option value="4">4.0x</option>
<option value="2">2.0x</option>
<option value="1">1.0x</option>
<option value="0.5">0.5x</option>
</select>
</div>
`;

// 在 </body> 标签前添加 html 代码
document.body.insertAdjacentHTML('beforeend', html);

添加外部 JavaScript 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 添加自定义的样式
* @param {*} src 外部JavaScript 文件 url
* @param {*} cb 在脚本加载并执行完成时触发的函数
* @param {*} onerror 处理发生在脚本加载期间的错误的函数
* @returns
*/
function addLinkScript(src, cb, onerror) {
let script = document.createElement("script");
script.type = "text/javascript";
script.src = src;
console.log("addScript", script)
document.querySelector('head').appendChild(script);
script.onload = typeof cb === "function" ? cb : function () { };
script.onerror = typeof onerror === "function" ? onerror : function () { };
}

查找嵌套的 iframe 中的元素

如果网页内包含的 iframe 元素时, 其内部元素不能被直接定位查找, 查找iframe 内部元素方法如下:

1
2
3
4
5
6
7
document.querySelectorAll('iframe').forEach( item =>
console.log(item.contentWindow.document.body.querySelectorAll('a'))
);

// 或者
document.querySelector("iframe").contentWindow.document.querySelector("button") ;

等待指定的时间

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 等待指定的时间, 调用时在函数名前加上: await
* @param {*} s 等待时间,单位:秒
* @returns
*/
async function waitTime(s) {
return new Promise((resolve, reject) => {
setTimeout(() => {
return resolve();
}, s * 1000);
});
}

延迟查找元素

有一些网页元素不能被直接定位查找, 例如使用 JavaScript 框架渲染或是从服务器读取数据后渲染的网页(如Vue.js)、 部分添加了反爬虫措施的网页(故意等待几秒钟后才显示内容)等。这些网页的脚本可以在等到某个特定元素出现后再开始执行, 方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/**
* 延迟查找元素
* @param {*} selector 被查找元素的选择器
* @param {*} parent 父元素, 默认为 document
* @param {*} timeout 最长查找时间, 单位 毫秒
*/
async function untilLoaded(selector, parent = document, timeout = 10 * 1000) {
return new Promise((resolve) => {

// 如果能找到元素, 直接返回
let result = parent.querySelector(selector);
if (result) {
// 此处不能缺少 return, 不然后续代码会继续执行
return resolve(result);
}
let timer;

// 找不到元素, 使用 MutationObserver 监听元素变动时目标是否出现
const observer = new MutationObserver((mutations) => {
for (let mutation of mutations) {
//仅搜寻新增的dom元素
for (let addedNode of mutation.addedNodes) {
//判断单个元素是否为元素类型。
if (addedNode instanceof Element) {
//matches函数判断元素是否符合选择器
//如果为真则返回该元素,如果不为真则进行querySelector对其子树进行搜寻
//matches的意义在于检查自身
//因为queryselector检查的是子级,无法检测自身是否是匹配元素。
result = addedNode.matches(selector)
? addedNode
: addedNode.querySelector(selector);
//如果搜寻到就解除监听,删除超时的定时器并返回结果
if (result) {
observer.disconnect();
timer && clearTimeout(timer);
return resolve(result);
}
}
}
}
});
//监听子元素改变,并监听该元素下的所有Dom元素
observer.observe(parent, {
childList: true,
subtree: true,
});
timer = setTimeout(() => {
observer.disconnect();
timer && clearTimeout(timer);
console.log('查找元素超时, 找不到目标元素:', selector);
return resolve(null);
}, timeout);
});
}

事件劫持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const oldEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function (...args) {

const events = ['visibilitychange', 'blur', 'copy', 'contextmenu', 'selectstart'];
console.log('args:', args);
if(window.onblur!==null){
window.onblur=null;
}
if(window.oncopy!==null){
window.oncopy=null;
}
if(window.oncontextmenu!==null){
window.oncontextmenu=null;
}
if(window.onselectstart!==null){
window.onselectstart=null;
}

if (args.length !== 0 && events.includes(args[0])) {
console.log(`劫持 ${args[0]} 事件成功! `)
return;
}

return oldEventListener.call(this, ...args)
}