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 操作,提高代码的健壮性和可维护性。

相关推荐
代码村新手18 分钟前
C++-类和对象(中)
java·开发语言·c++
慧一居士24 分钟前
Vite 中配置环境变量方法及完整示例
前端·vue.js
梦因you而美29 分钟前
XPath 元素定位全方位技术文档
javascript·xpath·xpath定位
葵花楹34 分钟前
【JAVA课设】【游戏社交系统】
java·开发语言·游戏
天意pt40 分钟前
Idempotency 幂等性 - 点赞和投票功能
前端·javascript·express
赵谨言1 小时前
Python串口的三相交流电机控制系统研究
大数据·开发语言·经验分享·python
民乐团扒谱机1 小时前
【微实验】数模美赛备赛:多目标优化求解实战(MATLAB实现,以流水车间调度为例)
开发语言·数学建模·matlab·甘特图·遗传算法·多目标优化·优化模型
weixin_427771611 小时前
cursor 智能commit
前端
努力的小陈^O^1 小时前
问题:Spring循环依赖问题排查与解决
java·开发语言·前端
Ccjf酷儿2 小时前
C++语言程序设计 (郑莉)第十章 泛型程序设计与C++标准模板库
开发语言·c++