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

[油猴脚本开发指南]MutationObserver简易例子

[复制链接]

159

主题

1105

帖子

618

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
618
发表于 2021-9-21 15:26:49 | 显示全部楼层 | 阅读模式

前言

为了防止大家走马观花式的学习,我大概写了一个常见的触发例子,用来学习他的使用以及一些属性

源码如下

<!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的操作如下

    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

这时候可以观察到第一个参数为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

那我们再观察位置1的对象

图片.png

这里我们可以看到相对0没有什么较大的变化,唯一的区别就是addedNodes插入了一个#text,这个对象的触发是来自

node.innerText=index

我们更改了node内的内容。

那我们接下来再观察位置2的对象

图片.png

这里看到type变成了attributes,这是一个属性的变化,target是我们要操作的对象,其他没有什么太大的变化,attributeName返回了被修改的属性名,也就是class。这个对象是由node.className='66666'所触发的。

接下来我们观察位置3的对象

图片.png

这个对象由node.appendChild(child)触发

这里依然没什么特别的变化,唯一的区别就是previousSibiling是一个#text '0'是为什么?

previousSibiling是一个同层前一位置的元素指向。

我们之前在node.innnerText=index代码中

创建了一个#text节点并插入到了node节点下

所以node节点下存在一个text节点

这时候再对node节点下插入一个div,div前存在一个#text节点,所以自然previousSibiling指向前一个节点就指向text节点啦!

接下来来看位置4的对象

图片.png

这里没什么特别的地方,由代码node.appendChild(child2)所触发

接下来看位置5的对象

图片.png

这里我们可以发现nextSibling和previousSibling都有值了,这是为什么?

previousSibling是前一个同层元素

而nextSibling是后一个同层元素

因为我们之前用appedchild函数插入元素,不停的插入到目标节点内的子节点的最后一位

而这次我们使用了insertBefore,他则是插入我们执行元素的之中,所以他的后方存在同层元素,自然nextSibling就不是null了

接下来我们来看位置6的对象

图片.png

相比也没有较为特殊的,这个对象是由document.querySelector('#test').removeChild(node)所触发,删除了相应的元素

所以在removeNodes显示了这个元素。

结语

那么到这里我们已经介绍和使用了大部分的地方。

还有attributeNamespace以及oldValue没有使用过,这两个个人认为相对较为少见,所以就不多加尝试了。

attributeNamespace我大概查阅了一下资料,大概意思是针对XML命名空间所建立的属性,oldValue在前文的介绍文章中也已经介绍过了,有兴趣可以自己尝试。

撒花~

图片.png
混的人。

4

主题

99

帖子

36

积分

新手上路

Rank: 1

积分
36

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

发表于 2021-9-21 20:41:15 | 显示全部楼层
大佬的教程九浅一深,通俗易懂,非常咳嗽
回复

使用道具 举报

159

主题

1105

帖子

618

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
618
发表于 2021-9-22 10:46:54 | 显示全部楼层
脚本体验师001 发表于 2021-9-21 20:41
大佬的教程九浅一深,通俗易懂,非常咳嗽

非常咳嗽!
混的人。
回复

使用道具 举报

发表回复

本版积分规则

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