JavaScript中MutationObServer监听DOM元素详情


Posted in Javascript onNovember 27, 2021

一、基本使用

可以通过MutationObserver构造函数实例化,参数是一个回调函数。

let observer = new MutationObserver(() => console.log("change"));

console.log(observer);

observer对象原型链如下:

MutationObserver实例:

JavaScript中MutationObServer监听DOM元素详情

可以看到有disconnectobservertakeRecords方法。

1. observer方法监听

observer方法用于关联DOM元素,并根据相关设置进行监听。

语法如下:

// 接收两个参数

observer(DOM元素, MutationObserverInit对象);

其中:

  • 第一个参数DOM元素就是页面元素,比如:body、div等。
  • 第二个参数就是设置要监听的范围。比如:属性、文本、子节点等,是一个键值对数组。

示例1,监听body元素class的变化:

let observer = new MutationObserver(() => console.log("change"));

// 监听body元素的属性变化

observer.observe(document.body, {

    attributes: true

});

// 更改body元素的class,会异步执行创建MutationObserver对象时传入的回调函数

document.body.className = "main";

console.log("修改了body属性");

// 控制台输出:

//    修改了body属性

//    change

上面 change 的输出是在 修改了body属性 之后,可见注册的回调函数是异步执行的,是在后面执行的。

2. 回调函数增加MutationRecord实例数组参数

现在回调函数非常简单,就是输出一个字符串,看不出到底发生了什么变化。

其实回调函数接收一个 MutationRecord 实例数组,实务中可以通过这个查看详细的信息。

let observer = new MutationObserver(

    // 回调函数是一个 MutationRecord 实例数组。格式如下:

    //     [MutationRecord, MutationRecord, MutationRecord, ...]

    (mutationRecords) => console.log(mutationRecords)

);

observer.observe(document.body, {

    attributes: true

});

document.body.className = "main";

console.log("修改了body属性");

// 控制台输出:

//    修改了body属性

//     (1) [MutationRecord]

其中 mutationRecords信息 如下:

MutationRecord实例

JavaScript中MutationObServer监听DOM元素详情

其中几个比较关键的信息:

  • attributeName 表示修改的属性名称
  • target 修改的目标
  • type 类型

如果多次修改body的属性,那么会有多条记录:

// MutationRecord

let observer = new MutationObserver(

    // 回调函数接收一个 MutationRecord 实例,是一个数组。

    (mutationRecords) => console.log(mutationRecords)

);

observer.observe(document.body, {

    attributes: true

});

 

// 修改三次

document.body.className = "main";

document.body.className = "container";

document.body.className = "box";

 

// 控制台打印如下:

//     (3) [MutationRecord, MutationRecord, MutationRecord]

注意:

这里不是修改一次就执行一次回调,而是每修改一次就往 mutationRecords 参数加入一个 MutationRecord 实例,最后执行一次回调打印出来。

如果修改一次就执行一次回调,那么性能就会比较差。

3. disconnect方法终止回调

如果要终止回调,可以使用disconnect方法。

let observer = new MutationObserver(

    (mutationRecords) => console.log(mutationRecords)

);

observer.observe(document.body, {

    attributes: true

});

// 第一次修改

document.body.className = "main";

 

// 终止

observer.disconnect();

 

// 第二次修改

document.body.className = "container";

 

// 没有日志输出

这里没有日志输出,包括第一次修改也没有日志输出,因为回调函数的执行是异步的,是在最后执行的。后面把observer终止了,所以就不会执行了。

可以用setTimeout控制最后才终止,这样回调就会正常执行。

let observer = new MutationObserver(

    (mutationRecords) => console.log(mutationRecords)

);

observer.observe(document.body, {

    attributes: true

});

// 第一次修改

document.body.className = "main";

 

// 终止

setTimeout(() => {

    observer.disconnect();

    // 第三次修改,下面修改不会回调了

    document.body.className = "container";

}, 0);

 

// 第二次修改

document.body.className = "container";

 

// 页面输出:

//    (2) [MutationRecord, MutationRecord]

终止之后再启用

终止了之后可以再次启动,请看下面示例:

let observer = new MutationObserver(

    (mutationRecords) => console.log(mutationRecords)

);

observer.observe(document.body, {

    attributes: true

});

// 第一次修改,会入 mutationRecords 数组

document.body.className = "main";

// 终止

setTimeout(() => {

    observer.disconnect();

    // 第二次修改,因为终止了,下面修改不会入 mutationRecords 数组

    document.body.className = "container";

}, 0);

setTimeout(() => {

    // 再次启用

    observer.observe(document.body, {

        attributes: true

    });

    // 修改body属性,会入 mutationRecords 数组

    document.body.className = "container";

}, 0);

 

// 控制台输出:

//    [MutationRecord]

//    [MutationRecord]

这边回调函数是执行了两次,打印了两个,其中:

  • 第一个输出是在第一次修改,后面没有同步代码了,就执行了回调。
  • 第二个输出是在第三次修改,因为重新启用了,所以就正常执行了回调。

第二次修改,因为observer被终止了,所以修改body的属性不会入 mutationRecords 数组。

4. takeRecords方法获取修改记录

如果希望在终止observer之前,对已有的 mutationRecords 记录进行处理,可以用takeRecords方法获取。

let observer = new MutationObserver(

    (mutationRecords) => console.log(mutationRecords)

);

observer.observe(document.body, {

    attributes: true

});

 

// 第一次修改,会入 mutationRecords 数组

document.body.className = "main";

// 第二次修改,会入 mutationRecords 数组

document.body.className = "container";

// 第三次修改,会入 mutationRecords 数组

document.body.className = "box";

// 取到修改记录,可以对其进行处理

let mutationRecords =  observer.takeRecords();

console.log(mutationRecords);

// 控制台打印:

//     (3) [MutationRecord, MutationRecord, MutationRecord]

console.log(observer.takeRecords());

// 控制台打印:

//    []

// 终止

observer.disconnect();

二、监听多个元素

上面监听都是只有一个元素,如果要监听多个元素可以复用MutationObserver实例

let observer = new MutationObserver(

    (mutationRecords) => console.log(mutationRecords)

);

// 创建 div1 元素,并监听

let div1 = document.createElement("div");

observer.observe(div1, {

    attributes: true

});

div1.id = "box1";

 

// 创建div2并监听

let div2 = document.createElement("div");

observer.observe(div2, {

    attributes: true

});

div2.id = "box2";

 

// 控制台打印:

//    (2) [MutationRecord, MutationRecord]

控制台打印了两个MutationRecord,其中:

  • 第一个 MutationRecord 就是 div1 的id属性修改记录。
  • 第二个 MutationRecord 就是 div2 的id属性修改记录。

其他使用方式和上面的类似。

三、监听范围MutationObserverInit对象

上面的监听都是监听属性,当然也可以监听其他的东西,比如:文本、子节点等。

1. 观察属性

上面的例子都是观察元素自有的属性,这里再举一个自定义属性的例子。

let observer = new MutationObserver(

    (mutationRecords) => console.log(mutationRecords)

);

observer.observe(document.body, {

    attributes: true

});

// 修改自定义的属性

document.body.setAttribute("data-id", 1);

 

// 控制台打印:

//    [MutationRecord]

修改自定义的属性一样会加入到 mutationRecords 数组。

另外值的一提的是 data-id 经常用来给元素标记一些数据啥的,如果发生变化,程序就可以监听到,就可以处理一些相应的逻辑。

attributeFilter过滤:

如果要监听指定的属性变化,可以用 attributeFilter 过滤。

let observer = new MutationObserver(

    (mutationRecords) => console.log(mutationRecords)

);

observer.observe(document.body, {

    attributes: true,

    // 设置白名单

    attributeFilter: ["data-id"]

});

 

// 修改白名单 attributeFilter 内的属性,会入 mutationRecords

document.body.setAttribute("data-id", 1);

 

// 修改不在白名单 attributeFilter 内的属性,不会入 mutationRecords

document.body.setAttribute("class", "main");

 

// 控制台打印:

//    [MutationRecord]

attributeOldValue记录旧值

如果要记录旧值,可以设置 attributeOldValue true

let observer = new MutationObserver(

    // MutationRecord对象中oldValue表示旧值

    (mutationRecords) => console.log(mutationRecords.map((x) => x.oldValue))

);

observer.observe(document.body, {

    attributes: true,

    attributeOldValue: true,

});

// 第一次修改,因为原来没有值,所以旧值 oldValue = null

document.body.setAttribute("class", "main");

// 第二次修改,因为前面有改了一次,所以旧值 oldValue = main

document.body.setAttribute("class", "container");

 

// 控制台打印:

//    (2) [null, 'main']

2. 观察文本

观察文本设置 characterDatatrue 即可,不过只能观察文本节点。

请看如下示例:

<!-- 一个性感的div -->

<div id="box">Hello</div>

<script type="text/javascript">

    let observer = new MutationObserver(

        (mutationRecords) => console.log(mutationRecords)

    );

    // 获取文本节点

    let textNode = document.getElementById("box").childNodes[0];

    observer.observe(textNode, {

        // 观察文本变化

        characterData: true

    });

    // 修改文本

    textNode.textContent = "Hi";

 

    // 控制台打印:

    //    [MutationRecord]

</script>

如果直接监听div元素,那么是不生效的:

<!-- 一个性感的div -->

<div id="box">Hello</div>

<script type="text/javascript">

    let observer = new MutationObserver(

        (mutationRecords) => console.log(mutationRecords)

    );

    // 监听div不会生效

    let box = document.getElementById("box");

    observer.observe(box, {

        characterData: true

    });

    box.textContent = "Hi";

 

    // 控制台无输出

</script>

characterDataOldValue记录旧值:

如果要记录文本旧值,可以设置 characterDataOldValuetrue

<!-- 一个性感的div -->

<div id="box">Hello</div>

 

<script type="text/javascript">

    let observer = new MutationObserver(

        (mutationRecords) => console.log(mutationRecords.map((x) => x.oldValue))

    );

    // 获取文本节点

    let textNode = document.getElementById("box").childNodes[0];

    observer.observe(textNode, {

        // 观察文本变化

        characterData: true,

        // 保留旧数据

        characterDataOldValue: true,

    });

    // 修改文本两次

    textNode.textContent = "Hi";

    textNode.textContent = "Nice";

 

    // 控制台打印:

    //    (2) ['Hello', 'Hi']

</script>

因为div内的内容原本为Hello,先修改为Hi,又修改为Nice,所以两次修改的旧值就为:Hello 和 Hi 了。

3. 观察子节点

MutationObserver 实例也可以观察目标节点子节点的变化。

<!-- 一个性感的div -->

<div id="box">Hello</div>

<script type="text/javascript">

    let observer = new MutationObserver(

        (mutationRecords) => console.log(mutationRecords)

    );

    // 获取div

    let box = document.getElementById("box");

    observer.observe(box, {

        // 观察子节点变化

        childList: true,

    });

    // 添加元素

    let span = document.createElement("span")

    span.textContent = "world";

    box.appendChild(span);

 

    // 控制台打印:

    //    [MutationRecord]

</script>

MutationRecord中的addedNodes属性记录了增加的节点。

移除节点:

<!-- 一个性感的div -->

<div id="box">Hello</div>

 

<script type="text/javascript">

    let observer = new MutationObserver(

        (mutationRecords) => console.log(mutationRecords)

    );

 

    // 获取div

    let box = document.getElementById("box");

    observer.observe(box, {

        // 观察子节点变化

        childList: true,

    });

    // 移除第一个子节点,就是Hello文本节点

    box.removeChild(box.childNodes[0]);

 

    // 控制台打印:

    //    [MutationRecord]

</script>

MutationRecord中的removedNodes属性记录了移除的节点。

移动节点:

对于已有的节点进行移动,那么会记录两条MutationRecord记录,因为移动现有的节点是先删除,后添加。

<!-- 一个性感的div -->

<div id="box">Hello<span>world</span></div>

 

<script type="text/javascript">

    let observer = new MutationObserver(

        (mutationRecords) => console.log(mutationRecords)

    );

 

    // 获取div

    let box = document.getElementById("box");

    observer.observe(box, {

        // 观察子节点变化

        childList: true,

    });

    // 将span节点移动到Hello节点前面

    box.insertBefore(box.childNodes[1], box.childNodes[0]);

    // 移动节点,实际是先删除,后添加。

 

    // 控制台打印:

    //    (2) [MutationRecord, MutationRecord]

</script>

4. 观察子树

上面观察的节点都是当前设置的目标节点,比如body,就只能观察body元素和其子节点的变化。

如果要观察body及其所有后代节点的变化,那么可以设置subtree属性为true

<!-- 一个性感的div -->

<div id="box">Hello<span>world</span></div>

 

<script type="text/javascript">

    let observer = new MutationObserver(

        (mutationRecords) => console.log(mutationRecords)

    );

 

    let box = document.getElementById("box");

    observer.observe(box, {

        attributes: true,

        // 观察子树的变化

        subtree: true

    });

    // span元素的id属性变化就可以观察到

    box.childNodes[1].id = "text";

    // 控制台打印:

    //    [MutationRecord]

</script>

subtree设置为true后,不光div元素本身,span元素也可以观察到了。

总结:

  • 1. MutationObserver实例可以用来观察对象。
  • 2. MutationRecord实例记录了每一次的变化。
  • 3. 回调函数需要所有脚本任务完成后,才会执行,即采用异步方式。
  • 4. 可以观察的访问有属性、文本、子节点、子树。

到此这篇关于JavaScriptMutationObServer监听DOM元素详情的文章就介绍到这了,更多相关JavaScript中MutationObServer监听DOM元素内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
js跨域和ajax 跨域问题的实现思路
Sep 05 Javascript
JQuery获取各种宽度、高度(format函数)实例
Mar 04 Javascript
jquery制作漂亮的弹出层提示消息特效
Dec 23 Javascript
js实现格式化金额,字符,时间的方法
Feb 26 Javascript
js计算德州扑克牌面值的方法
Mar 04 Javascript
jQuery插件jcrop+Fileapi完美实现图片上传+裁剪+预览的代码分享
Apr 22 Javascript
举例详解Python中smtplib模块处理电子邮件的使用
Jun 24 Javascript
javascript实现C语言经典程序题
Nov 29 Javascript
vue.js入门(3)——详解组件通信
Dec 02 Javascript
JavaScript中的 attribute 和 jQuery中的 attr 方法浅析
Jan 04 Javascript
jquery+css3实现熊猫tv导航代码分享
Feb 12 jQuery
vue.js 双层嵌套for遍历的方法详解, 类似php foreach()
Sep 07 Javascript
JavaScript中document.activeELement焦点元素介绍
Nov 27 #Javascript
利用JavaScript写一个简单计算器
JavaScript中的宏任务和微任务详情
Nov 27 #Javascript
前端监听websocket消息并实时弹出(实例代码)
Vue实现跑马灯样式文字横向滚动
Nov 23 #Vue.js
利用js实现简单开关灯代码
Nov 23 #Javascript
详解Vue的列表渲染
Nov 20 #Vue.js
You might like
调试PHP程序的多种方法介绍
2014/11/06 PHP
PHP数组游标实现对数组的各种操作详解
2016/01/26 PHP
PHPCMS忘记后台密码的解决办法
2016/10/30 PHP
PHP中用mysqli面向对象打开连接关闭mysql数据库的方法
2016/11/05 PHP
PHP快速推送微信模板消息
2017/04/14 PHP
ThinkPHP5.0框架验证码功能实现方法【基于第三方扩展包】
2019/03/11 PHP
超简单的jquery的AJAX用法
2010/05/10 Javascript
Javascript selection的兼容性写法介绍
2013/12/20 Javascript
jquery中get和post的简单实例
2014/02/04 Javascript
JS获得浏览器版本和操作系统版本的例子
2014/05/13 Javascript
JS数组的遍历方式for循环与for...in
2014/07/31 Javascript
javascript复制粘贴与clipboardData的使用
2014/10/16 Javascript
node.js中的path.join方法使用说明
2014/12/08 Javascript
Javascript中的几种URL编码方法比较
2015/01/23 Javascript
JavaScript动态检验密码强度的实现方法
2016/11/09 Javascript
快速理解 JavaScript 中的 LHS 和 RHS 查询的用法
2017/08/24 Javascript
JavaScript中Object值合并方法详解
2017/12/22 Javascript
大转盘抽奖小程序版 转盘抽奖网页版
2020/04/16 Javascript
详解如何搭建mpvue框架搭配vant组件库的小程序项目
2019/05/16 Javascript
使用js实现单链解决前端队列问题的方法
2020/02/03 Javascript
PyCharm使用教程之搭建Python开发环境
2016/06/07 Python
python距离测量的方法
2018/03/06 Python
pandas多级分组实现排序的方法
2018/04/20 Python
Python实现数据可视化看如何监控你的爬虫状态【推荐】
2018/08/10 Python
Python 通过调用接口获取公交信息的实例
2018/12/17 Python
Django获取应用下的所有models的例子
2019/08/30 Python
tensorboard 可以显示graph,却不能显示scalar的解决方式
2020/02/15 Python
canvas实现手机的手势解锁的步骤详细
2020/03/16 HTML / CSS
爱护公共设施演讲稿
2014/09/13 职场文书
大专生自我鉴定怎么写
2014/09/16 职场文书
党员贯彻十八大精神思想汇报范文
2014/10/25 职场文书
2014年后备干部工作总结
2014/12/08 职场文书
消防隐患整改通知书
2015/04/22 职场文书
运动会加油稿30字
2015/07/21 职场文书
企业愿景口号
2015/12/25 职场文书
创业计划书之服装
2019/10/07 职场文书