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获取服务器时间的两个简单方法
Jan 08 Javascript
javascript结合ajax读取txt文件内容
Dec 05 Javascript
使用angular写一个hello world
Jan 23 Javascript
js实现动态加载脚本的方法实例汇总
Nov 02 Javascript
微信小程序 Tab页切换更新数据
Jan 05 Javascript
一篇看懂vuejs的状态管理神器 vuex状态管理模式
Apr 20 Javascript
vue vuex vue-rouert后台项目——权限路由(适合初学)
Dec 29 Javascript
jQuery 导航自动跟随滚动的实现代码
May 30 jQuery
ionic使用angularjs表单验证(模板验证)
Dec 12 Javascript
vue--vuex详解
Apr 15 Javascript
javascript实现超好看的3D烟花特效
Jan 01 Javascript
Vue循环中多个input绑定指定v-model实例
Aug 31 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
PHPCMS的使用小结
2010/09/20 PHP
PHP中使用gettext来支持多语言的方法
2011/05/02 PHP
php数字转汉字代码(算法)
2011/10/08 PHP
php daddslashes()和 saddslashes()有哪些区别分析
2012/10/26 PHP
基于php-fpm 参数的深入理解
2013/06/03 PHP
php设计模式之单例模式使用示例
2014/01/20 PHP
使用PHP进行微信公众平台开发的示例
2015/08/21 PHP
laravel框架创建授权策略实例分析
2019/11/22 PHP
JS 自动安装exe程序
2008/11/30 Javascript
javascript获取隐藏dom的宽高 具体实现
2013/07/14 Javascript
js锁屏解屏通过对$.ajax进行封装实现
2014/07/31 Javascript
使用 js+正则表达式为关键词添加链接
2014/11/11 Javascript
原生javaScript实现图片延时加载的方法
2014/12/22 Javascript
原生js仿浏览器滚动条效果
2017/03/02 Javascript
详解有关easyUI的拖动操作中droppable,draggable用法例子
2017/06/03 Javascript
微信小程序 Buffer缓冲区的详解
2017/07/06 Javascript
CSS3结合jQuery实现动画效果及回调函数的实例
2017/12/27 jQuery
微信小程序使用map组件实现路线规划功能示例
2019/01/22 Javascript
JavaScript解析JSON数据示例
2019/07/16 Javascript
vue中提示$index is not defined错误的解决方式
2020/09/02 Javascript
wxpython 最小化到托盘与欢迎图片的实现方法
2014/06/09 Python
python实现图片变亮或者变暗的方法
2015/06/01 Python
浅谈Python中的zip()与*zip()函数详解
2018/02/24 Python
一条命令解决mac版本python IDLE不能输入中文问题
2018/05/15 Python
numpy.linspace函数具体使用详解
2019/05/27 Python
Django3.0 异步通信初体验(小结)
2019/12/04 Python
python批量替换文件名中的共同字符实例
2020/03/05 Python
CSS3字体效果的设置方法小结
2016/06/13 HTML / CSS
浅谈CSS3鼠标移入图片动态提示效果(transform)
2017/11/06 HTML / CSS
GUESS西班牙官方网上商城:美国服饰品牌
2017/03/15 全球购物
一百多行代码实现react拖拽hooks
2021/03/23 Javascript
单位领导证婚词
2014/01/14 职场文书
会计学专业学生的求职信范文
2014/01/27 职场文书
工伤赔偿协议书范本
2014/04/15 职场文书
党员民主评议自我评价
2014/10/20 职场文书
三八妇女节主持词
2015/07/04 职场文书