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

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

[复制链接]
  • TA的每日心情
    开心
    2023-2-28 23:59
  • 签到天数: 191 天

    [LV.7]常住居民III

    637

    主题

    5189

    回帖

    6073

    积分

    管理员

    非物质文化遗产社会摇传承人

    积分
    6073

    荣誉开发者管理员油中2周年生态建设者喜迎中秋

    发表于 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在前文的介绍文章中也已经介绍过了,有兴趣可以自己尝试。

    撒花~

    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道

    入驻了爱发电https://afdian.net/a/lihengdao666
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。
  • TA的每日心情
    开心
    2024-4-14 00:00
  • 签到天数: 119 天

    [LV.6]常住居民II

    29

    主题

    598

    回帖

    535

    积分

    专家

    积分
    535

    油中2周年生态建设者油中3周年挑战者 lv2

    发表于 2021-9-21 20:41:15 | 显示全部楼层
    大佬的教程九浅一深,通俗易懂,非常咳嗽
    入驻爱发电 让这世界充满爱 https://afdian.net/a/vpannice
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2023-2-28 23:59
  • 签到天数: 191 天

    [LV.7]常住居民III

    637

    主题

    5189

    回帖

    6073

    积分

    管理员

    非物质文化遗产社会摇传承人

    积分
    6073

    荣誉开发者管理员油中2周年生态建设者喜迎中秋

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

    非常咳嗽!
    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道

    入驻了爱发电https://afdian.net/a/lihengdao666
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。
    回复

    使用道具 举报

    发表回复

    本版积分规则

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