[油猴脚本开发指南]MutationObserver简易例子
# 前言为了防止大家走马观花式的学习,我大概写了一个常见的触发例子,用来学习他的使用以及一些属性
源码如下
```javascript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id='test'>
</div>
</body>
<script>
console.time('time')
let targetNode = document.querySelector('#test');
let observerOptions = {
childList: true, // 观察目标子节点的变化,添加或删除
attributes: true, // 观察属性变动
subtree: true, //默认是false,设置为true后可观察后代节点
}
function callback(mutationList, observer) {
debugger;
mutationList.forEach((mutation) => {
debugger;
switch(mutation.type) {
case 'childList':
/* 从树上添加或移除一个或更多的子节点;参考mutation.addedNodes 和mutation.removeNodes */
break;
case 'attributes':
/* mutation.target 中某个节点的一个属性值被更改;该属性名称在mutation.attributeName中,该属性之前的值为 mutation.oldValue */
break;
}
});
}
let observer = new MutationObserver(callback);
observer.observe(targetNode, observerOptions);
for (let index = 0; index < 1000; index++) {
let node=document.createElement("div")
document.querySelector('#test').appendChild(node)
node.innerText=index
node.className='66666'
let child=document.createElement("div")
child.innerText='child'
let child1=document.createElement("div")
child1.innerText='child1'
let child2=document.createElement("div")
child2.innerText='child2'
node.appendChild(child)
node.appendChild(child2)
node.insertBefore(child1,child2)
document.querySelector('#test').removeChild(node)
}
console.timeEnd('time')
</script>
</html>
```
我们逐步拆解这段代码
# 性能问题
由于Mutation Events在注册后属于一个同步代码,而MutationObserver属于异步代码,所以不存在性能方面的问题,我们可以直接忽略,经过时间性的测试也并无较大变化。
# 代码问题
```
let observer = new MutationObserver(callback);
observer.observe(targetNode, observerOptions);
```
我们首先对其注册了一个回调,并设置了targertnode为`#test`的节点,options内则设置了观察子节点变化、属性变化、以及拓展后代所有节点。
为了尽可能的触发大量的操作,所以我对dom的操作如下
```javascript
for (let index = 0; index < 1000; index++) {
let node=document.createElement("div")
document.querySelector('#test').appendChild(node)
node.innerText=index
node.className='66666'
let child=document.createElement("div")
child.innerText='child'
let child1=document.createElement("div")
child1.innerText='child1'
let child2=document.createElement("div")
child2.innerText='child2'
node.appendChild(child)
node.appendChild(child2)
node.insertBefore(child1,child2)
document.querySelector('#test').removeChild(node)
}
```
创建节点,插入该节点,设置内容为index,class名为6666,创建三个该节点的子节点,分别添加第一个和第三个,然后将第二个插入中间。最后删除该节点,将其循环一千次。
运行代码可以发现在debugger处停止执行
![图片.png](data/attachment/forum/202109/21/151623i3n0bnm3bc0dbdni.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "图片.png")
这时候可以观察到第一个参数为7000个MutationRecord对象的数组。
因为是异步代码,所以并不在执行操作的时候同步执行回调,而是将大量的操作整合成一个数组在某个时候统一发送给我们
由于我们反复插入了1000次相同操作,所以触发的MutationRecord对象个数应该也是同样的个数
也就是说每次循环触发了7000/1000=7个MutationRecord对象,数组由0开始,所以是数组位置0-6的MutationRecord对象\
我们先看位置0的
这句相当于
```
let node=document.createElement("div")
document.querySelector('#test').appendChild(node)
```
创建一个div并且插入进去,插入的元素会显示在addednodes的属性上,为什么现在就有66666这个属性了?因为我们显示的是异步数据,执行的命令早已全部执行完毕,我们只是在观察结果,而非注册Mutation Events那样的同步执行。
previousSibling是前一个位置的同层元素,因为里边没有东西但是可能存在一些空格,所以是# text " "
nextSibling是null,因为目前不存在后一个位置的同层元素
target是插入的目标元素
type这是目前的执行操作的属性类型
![图片.png](data/attachment/forum/202109/21/170135yoy65ozk8875c5od.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "图片.png")
那我们再观察位置1的对象
![图片.png](data/attachment/forum/202109/21/170659lpklffa7ayf7flez.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "图片.png")
这里我们可以看到相对0没有什么较大的变化,唯一的区别就是addedNodes插入了一个#text,这个对象的触发是来自
node.innerText=index
我们更改了node内的内容。
那我们接下来再观察位置2的对象
![图片.png](data/attachment/forum/202109/21/170841sz85jjiiczbjj6ww.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "图片.png")
这里看到type变成了attributes,这是一个属性的变化,target是我们要操作的对象,其他没有什么太大的变化,attributeName返回了被修改的属性名,也就是class。这个对象是由node.className='66666'所触发的。
接下来我们观察位置3的对象
![图片.png](data/attachment/forum/202109/21/171207bz1l85m9subnoerl.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "图片.png")
这个对象由`node.appendChild(child)`触发
这里依然没什么特别的变化,唯一的区别就是previousSibiling是一个#text '0'是为什么?
previousSibiling是一个同层前一位置的元素指向。
我们之前在node.innnerText=index代码中
创建了一个#text节点并插入到了node节点下
所以node节点下存在一个text节点
这时候再对node节点下插入一个div,div前存在一个#text节点,所以自然previousSibiling指向前一个节点就指向text节点啦!
接下来来看位置4的对象
![图片.png](data/attachment/forum/202109/21/171442v1zzx2fu2frhqo1w.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "图片.png")
这里没什么特别的地方,由代码`node.appendChild(child2)`所触发
接下来看位置5的对象
![图片.png](data/attachment/forum/202109/21/171518m1mmnkk1nrmzsaka.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "图片.png")
这里我们可以发现nextSibling和previousSibling都有值了,这是为什么?
previousSibling是前一个同层元素
而nextSibling是后一个同层元素
因为我们之前用appedchild函数插入元素,不停的插入到目标节点内的子节点的最后一位
而这次我们使用了`insertBefore`,他则是插入我们执行元素的之中,所以他的后方存在同层元素,自然nextSibling就不是null了
接下来我们来看位置6的对象
![图片.png](data/attachment/forum/202109/21/171717wvnxn1x4vlyrklvl.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "图片.png")
相比也没有较为特殊的,这个对象是由`document.querySelector('#test').removeChild(node)`所触发,删除了相应的元素
所以在removeNodes显示了这个元素。
# 结语
那么到这里我们已经介绍和使用了大部分的地方。
还有**attributeNamespace**以及**oldValue**没有使用过,这两个个人认为相对较为少见,所以就不多加尝试了。
**attributeNamespace**我大概查阅了一下资料,大概意思是针对XML命名空间所建立的属性,**oldValue**在前文的介绍文章中也已经介绍过了,有兴趣可以自己尝试。
撒花~
大佬的教程九浅一深,通俗易懂,非常咳嗽 脚本体验师001 发表于 2021-9-21 20:41
大佬的教程九浅一深,通俗易懂,非常咳嗽
非常咳嗽!
页:
[1]