JavaScript MutationObserver用法( 监听DOM变化 )

在Vue的开发中,

Vue2 中会用侦听器watch来监听数据的变化。

Vue3 中使用 proxy 替代了defineProperty用来监听整个对象 ,性能大大提高。

那如果想监听DOM的变化呢,该怎么做呢?

JavaScript 提供了多种 API 来操作 DOM 结构。而在操作 DOM 时,经常需要监测 DOM 的变化,此时,MutationObserver 就显得非常有用。

一、MutationObserver 简介

MutationObserver 是 HTML5 引入的一种用于监听 DOM 树变化的接口。它可以在 DOM 树发生以下变化时执行回调函数:

  • 元素的子树发生变化(子节点的添加、删除或重排序)。
  • 元素的属性发生变化。
  • 元素的文本内容发生变化。

与传统的 DOM 事件(如 DOMSubtreeModified、DOMNodeInserted、DOMNodeRemoved 等)相比,MutationObserver 提供了更高效和更灵活的 API。

原理:

MutationObserver 通过异步方式监测 DOM 变化,这意味着当 DOM 变化发生时,MutationObserver 不会立即执行回调函数,而是将这些变化存入一个队列中,并在本轮 JavaScript 执行完之后,才批量处理这些变化。这种异步批量处理的机制,使得 MutationObserver 更加高效。

二、MutationObserver 基础用法

创建一个 MutationObserver 实例,传入一个回调函数。

使用 observe 方法开始监听目标节点及其相关的变化。

当不再需要监听时,使用 disconnect 方法停止观察。停止MutationObserver对象的观察,且清空所有的MutationRecord对象。

observe 配置项:

接受两个参数:目标节点和一个配置对象。配置对象用于指定要观察哪些类型的变化。常用配置项包括:

|-----------------------|--------------------------------------------|-------|
| 属性 | 说明 | 默认值 |
| subtree | 当设置为 true 时,监视目标节点及其所有后代节点的变化 | false |
| childList | 当目标节点(如果 subtree 为 true,则包含子孙节点)添加或删除时触发回调 | false |
| attributes | 当元素的属性变化时触发回调 | false |
| attributeFilter | 要监视的特定属性名称的数组。如果未包含此属性,则对所有属性的更改都会触发变动通知 | 无默认值 |
| characterData | 设为 true 以监视指定目标节点或子节点树中节点所包含的字符数据的变化 | false |
| attributeOldValue | 当属性变化时,记录变化前的属性值 | false |
| characterDataOldValue | 当文本节点变化时,记录变化前的文本内容 | false |

代码实现:

复制代码
// 1. 创建一个 MutationObserver 实例,并传入回调函数
const observer = new MutationObserver((mutationsList, observer) => {
  mutationsList.forEach(mutation => {
	// 处理逻辑
    if (mutation.type === 'childList') {
      console.log('已添加或删除子节点。');
    } else if (mutation.type === 'attributes') {
      console.log('修改了 ' + mutation.attributeName + ' 属性');
    }
  }
});

// 2. 开始监听目标节点
const elementNode = document.querySelector('#myDiv');
const config = { attributes: true, childList: true, subtree: true };

observer.observe(elementNode, config);

// 3. 停止监听
// observer.disconnect();
三、MutationObserver 使用场景
1:监控动态内容加载

页面数据通过axios接口请求动态加载到页面上的。可以使用 MutationObserver 监控动态内容的加载,在加载完成后进行一些操作(如:绑定事件、修改样式等)

复制代码
// 动态内容加载容器
const contentEle = document.querySelector('.content');

// 创建观察者实例
const observer = new MutationObserver((mutationsList) => {
  for (let mutation of mutationsList) {
    if (mutation.type === 'childList') {
      console.log('New content has been loaded:', mutation.addedNodes);
      // 对新增的节点进行一些操作
      mutation.addedNodes.forEach(node => {
        if (node.nodeType === Node.ELEMENT_NODE) {
          // 绑定事件,或执行其他逻辑
          node.addEventListener('click', () => console.log('New element clicked!'));
        }
      });
    }
  }
});

// 配置选项
const config = { childList: true, subtree: true };

// 开始监控
observer.observe(contentEle, config);
2:监控属性变化

假如需要页面的某个元素 data-v-status 属性发生变化时做出响应处理。MutationObserver 可以轻松实现这一需求。

复制代码
// 目标节点
const statusEle = document.querySelector('#status');

// 创建观察者实例
const observer = new MutationObserver((mutationsList) => {
  for (let mutation of mutationsList) {
    if (mutation.type === 'attributes' && mutation.attributeName === 'data-v-status') {
      console.log('Status changed to:', statusEle.getAttribute('data-v-status'));
    }
  }
});

// 配置选项
const config = { attributes: true, attributeFilter: ['data-v-status'] };

// 开始监控
observer.observe(statusEle, config);
3:批量修改 DOM, 优化页面性能

批量向Dom节点中添加了 N多个子节点, MutationObserver 会统一处理这些 DOM 变化,有效减少了重绘和重排操作,优化页面性能。

复制代码
// 目标节点
const containerEle = document.querySelector('#container');

// 创建观察者实例
const observer = new MutationObserver((mutationsList) => {
  mutationsList.forEach(mutation => {
    if (mutation.type === 'childList') {
      // 批量处理逻辑
      console.log('批量处理DOM更改');
    }
  });
});

// 配置选项
const config = { childList: true, subtree: true };

// 开始监控
observer.observe(containerEle, config);

// 批量修改 DOM
const fragment = document.createDocumentFragment();
for (let i = 0; i < 500; i++) {
  const newDiv = document.createElement('div');
  newDiv.textContent = `Item ${i}`;
  fragment.appendChild(newDiv);
}
containerEle.appendChild(fragment);
4:删除恶意Javascript脚本, 防止 DOM 劫持

在一些恶意脚本或第三方插件注入的情况下,DOM 结构可能会被劫持。可以使用 MutationObserver 检测 DOM 结构的异常变化,从而做出防护措施处理。

复制代码
 // 目标节点
const body = document.body;

// 创建观察者实例
const observer = new MutationObserver((mutationsList) => {
  for (let mutation of mutationsList) {
    if (mutation.type === 'childList') {
      mutation.addedNodes.forEach(node => {
        if (node.nodeType === Node.ELEMENT_NODE && node.tagName === 'SCRIPT') {
          console.warn('检测到潜在的恶意脚本!');
          // 移除恶意脚本
          node.parentNode.removeChild(node);
        }
      });
    }
  }
});

// 配置选项
const config = { childList: true, subtree: true };

// 开始监控
observer.observe(body, config);
四、结语

MutationObserver 是一个非常强大的 API,额外提供了一种高效、灵活的方式来监听和响应 DOM 变化。相比较于传统 DOM 事件监听器的诸多局限性,它通过异步、批量的方式处理 DOM 变化,极大的提高了性能和效率。在实际开发中,合理使用 MutationObserver 可以帮助我们更好地控制 DOM 操作,提高代码的健壮性和可维护性。

相关推荐
代码搬运媛7 小时前
Jest 测试框架详解与实现指南
前端
为何创造硅基生物8 小时前
C语言 结构体内存对齐规则(通俗易懂版)
c语言·开发语言
吃好睡好便好8 小时前
在Matlab中绘制横直方图
开发语言·学习·算法·matlab
星寂樱易李8 小时前
iperf3 + Python-- 网络带宽、网速、网络稳定性
开发语言·网络·python
counterxing8 小时前
我把 Codex 里的 Skills 做成了一个 MCP,还支持分享
前端·agent·ai编程
仰泳之鹅8 小时前
【C语言】自定义数据类型2——联合体与枚举
c语言·开发语言·算法
wangqiaowq8 小时前
windows下nginx的安装
linux·服务器·前端
之歆8 小时前
DAY_12JavaScript DOM 完全指南(二):实战与性能篇
开发语言·前端·javascript·ecmascript
发现一只大呆瓜9 小时前
Vite凭什么这么快?3分钟带你彻底搞懂 Vite 热更新的幕后黑手
前端·面试·vite
Maimai108089 小时前
React如何用 @microsoft/fetch-event-source 落地 SSE:比原生 EventSource 更灵活的实时推送方案
前端·javascript·react.js·microsoft·前端框架·reactjs·webassembly