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 相关文章推荐
DB.ASP 用Javascript写ASP很灵活很好用很easy
Jul 31 Javascript
使用js Math.random()函数生成n到m间的随机数字
Oct 09 Javascript
浅谈javascript实现八大排序
Apr 27 Javascript
jQuery中animate动画第二次点击事件没反应
May 07 Javascript
JavaScript制作简单的日历效果
Mar 10 Javascript
Vuejs第八篇之Vuejs组件的定义实例解析
Sep 05 Javascript
Vue.js双向绑定操作技巧(初级入门)
Dec 27 Javascript
Vue数据驱动模拟实现5
Jan 13 Javascript
js实现日历的简单算法
Jan 24 Javascript
纯js实现图片匀速淡入淡出效果
Aug 22 Javascript
Vue框架之goods组件开发详解
Jan 25 Javascript
mpvue实现微信小程序快递单号查询代码
Apr 03 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
WinXP + Apache +PHP5 + MySQL + phpMyAdmin安装全功略
2006/07/09 PHP
PHP Squid中可缓存的动态网页设计
2008/09/17 PHP
php面向对象全攻略 (十六) 对象的串行化
2009/09/30 PHP
smarty巧妙处理iframe中内容页的代码
2012/03/07 PHP
php利用smtp类实现电子邮件发送
2015/10/30 PHP
PHP中的session安全吗?
2016/01/22 PHP
php $_SESSION会员登录实例分享
2021/01/19 PHP
ThinkPHP框架实现导出excel数据的方法示例【基于PHPExcel】
2018/05/12 PHP
php 自定义函数实现将数据 以excel 表格形式导出示例
2019/11/13 PHP
JavaScript读取中文cookie时的乱码问题的解决方法
2009/10/14 Javascript
浅析JavaScript原型继承的陷阱
2013/12/03 Javascript
jQuery制作可自定义大小的拼图游戏
2015/03/30 Javascript
jQuery选择器源码解读(一):Sizzle方法
2015/03/31 Javascript
js闭包引起的事件注册问题介绍
2016/03/29 Javascript
原生js实现无限循环轮播图效果
2017/01/20 Javascript
详解Angular 中 ngOnInit 和 constructor 使用场景
2017/06/22 Javascript
Node解决简单重复问题系列之Excel内容的获取
2018/01/02 Javascript
详解如何制作并发布一个vue的组件的npm包
2018/11/10 Javascript
记一次用vue做的活动页的方法步骤
2019/04/11 Javascript
Vue+Spring Boot简单用户登录(附Demo)
2020/11/12 Javascript
Python编程之微信推送模板消息功能示例
2017/08/21 Python
python3实现字符串的全排列的方法(无重复字符)
2018/07/07 Python
使用Python获取并处理IP的类型及格式方法
2018/11/01 Python
python使用pdfminer解析pdf文件的方法示例
2018/12/20 Python
通过PHP与Python代码对比的语法差异详解
2019/07/10 Python
tensorflow2.0保存和恢复模型3种方法
2020/02/03 Python
Django集成MongoDB实现过程解析
2020/12/01 Python
Web前端页面跳转并取到值
2017/04/24 HTML / CSS
一套软件测试笔试题
2014/07/25 面试题
理工学院学生自我鉴定
2014/02/23 职场文书
2014县委书记党的群众路线教育实践活动对照检查材料思想汇报
2014/09/22 职场文书
师德师风个人整改措施
2014/10/27 职场文书
2014年驾驶员工作总结
2014/11/18 职场文书
会议通知格式范文
2015/04/15 职场文书
2015年高校教师个人工作总结
2015/05/25 职场文书
董事长开业致辞
2015/07/29 职场文书