[油猴脚本开发指南]延迟查询元素代码解读
# 前言本文是在第20节课时,由cxxjackie编写的延迟查询代码的解读,意在带大家学习如何阅读他人代码,以及养成一个良好的学习习惯。
为一个加强篇,如果学有余力可以尝试学习。目前没有能力可以跳过。
代码地址https://bbs.tampermonkey.net.cn/thread-835-1-1.html文章下方
# 解读
代码如下
```javascript
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);
}
}
});
}
```
调用这个函数传入了父元素、匹配的css内容,超时时间
我们可以看到return new Promise,证明了这是一个返回了promise包装的函数。
只有调用resolve返回的promise才会完成执行
```javascript
let result = parent.querySelector(selector);
if (result) return resolve(result);
```
首先进行了一次查询,如果存在则返回内容。
```javascript
const mutationObserver = window.MutationObserver || window.WebkitMutationObserver || window.MozMutationObserver;
```
这里是为了对不同浏览器进行兼容性处理,并返回一个可用的函数。
if (mutationObserver)
判断是否存在,如果存在则使用MutationObserver函数,不存在则使用addEventListener函数。
我们先看存在的条件下是如何运行的。
```javascript
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);
}
}
}
}
});
```
这里声明了一个观察器对象,并传入了一个函数,之前我们已经学过mutations是一个MutationRecord数组
这里对数组进行遍历
然后对每个MutationRecord下的addedNodes数组进行遍历,addedNodes是在监控的对象下插入的对象的数组
判断单个元素是否为元素类型。
然后进行addedNode.matches(selector)。
这里的matches是一个函数,用来进行选择器匹配,类似#test这样的css选择器匹配。
如果为真则返回该元素,如果不为真则进行querySelector对其进行搜寻。
然后返回结果
对结果进行判断,如果结果为真,则取消监听器并判断是否存在超时,如果存在超时则取消定时器循环
然后返回结果
```javascript
observer.observe(parent, {
childList: true,
subtree: true
});
```
这里类似于Mutation Events的Dom插入事件。
```javascript
if (timeout > 0) {
timer = setTimeout(() => {
observer.disconnect();
return resolve(null);
}, timeout);
}
```
这里判断是否存在超时,如果存在则启动settimeout定时器,在达到事件后无论是否找到元素
都结束操作并返回null。
那么到这里我们已经学习了MutationObserver函数存在的情况了,接下来我们看另一种不存在的情况。
```javascript
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);
}
}
};
```
声明了一个函数用于待会的DomNodeinserted函数的监听。
这里通过e.target获取到原对象元素。
判断是否为元素类型。
然后则进行与之前相似的判断。
并判断结果,结束EventListerner的监听,并根据情况清空定时器,然后返回结果。
```javascript
parent.addEventListener('DOMNodeInserted', listener, true);
```
在元素下监听DOMNodeInserted事件的触发。
```javascript
if (timeout > 0) {
timer = setTimeout(() => {
parent.removeEventListener('DOMNodeInserted', listener, true);
return resolve(null);
}, timeout);
}
```
这里已经是一个超时的判断,我们之前已经解释过了。
# 如何调用
这是两个调用实例,一个是异步的promise+then,一个是await堵塞至彻底返回结果
```javascript
function example1() {
getElement(document, '#test').then(element => {
//...
});
}
async function example2() {
const element = await getElement(document, '#test');
//...
}
```
# matches兼容性问题
matches存在兼容性问题,如果浏览器不支持则在代码开头加上
```javascript
Element.prototype.matches = Element.prototype.matches || Element.prototype.matchesSelector || Element.prototype.webkitMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.mozMatchesSelector;
```
这个问题可以参考https://developer.mozilla.org/zh-CN/docs/Web/API/Element/matches
# 补充问题
matches的意义在于检查自身,因为queryselector检查的是子级,无法检测自身是否是匹配元素。
ggnb!! ggnb!!{:4_93:} 我靠,简直是要爱上你们了,就喜欢这种又硬又能扛饿的代码,一夫当关大杀四方的硬度。大多情况竟都能轻松应对,值得一学 脚本体验师001 发表于 2021-10-1 22:33
我靠,简直是要爱上你们了,就喜欢这种又硬又能扛饿的代码,一夫当关大杀四方的硬度。大多情况竟都能轻松应 ...
{:4_96:}论坛大家对油猴研究真的贼他妈细...现在知识密集度搞得我都麻了 matches这里我补充一下,因为querySelector不匹配自身,即a.querySelector不会匹配a自己,所以matches就是判断a自己是否符合选择器。 李恒道 发表于 2021-10-1 22:42
论坛大家对油猴研究真的贼他妈细...现在知识密集度搞得我都麻了
呵呵,这才是追求知识的范儿,掐指一算,这个论坛是要火的一塌糊涂的最后 cxxjackie 发表于 2021-10-1 22:46
matches这里我补充一下,因为querySelector不匹配自身,即a.querySelector不会匹配a自己,所以matches就是 ...
你确定是个大佬,算过了 cxxjackie 发表于 2021-10-1 22:46
matches这里我补充一下,因为querySelector不匹配自身,即a.querySelector不会匹配a自己,所以matches就是 ...
这个没考虑到!
页:
[1]