MutationObserver概述及应用

前言:紧接上文前端性能监控一文中在FMP,FCP算法设计中需要使用到MutationObserver对象进行一些DOM监控的操作,本文详细介绍一下MutationObserver Api

简介

MutationObserver用于监控DOM树变化,提供了异步方法来监听DOM元素的增加、删除、属性变化操作。开发者可以借助此方法对DOM树变化做出相应的响应。

构造函数MutationObserver()

DOM 规范中的 MutationObserver() 构造函数—是 MutationObserver 接口内容的一部分—创建并返回一个新的观察器,它会在触发指定 DOM 事件时,调用指定的回调函数。MutationObserver 对 DOM 的观察不会立即启动;而必须先调用 observe() 方法来确定,要监听哪一部分的 DOM 以及要响应哪些更改。

observe参数:

  • target: 需要观测的目标节点
  • options:
    • attributes:是否监测元素的属性变化。
    • attributeOldValue:是否在属性变化时记录旧值。
    • attributeFilter:指定要监测的属性列表。
    • childList:是否监测子元素的添加或移除。
    • subtree:是否监测后代元素的变化。
    • characterData:是否监测文本节点的内容变化。
    • characterDataOldValue:是否在文本节点内容变化时记录旧值。
1
2
3
4
5
6
7
8
9
10
// 代码实例
var targetNode = document.querySelector("#someElement");
var observerOptions = {
childList: true, // 观察目标子节点的变化,是否有添加或者删除
attributes: true, // 观察属性变动
subtree: true, // 观察后代节点,默认为 false
};

var observer = new MutationObserver(callback);
observer.observe(targetNode, observerOptions);

常见的使用场景

  1. 动态内容加载
    当页面内容是异步加载或者说是动态生成时,可以使用MutationObserver来监控内容变化,并在变化后进行相应的处理,如页面更新,监听事件的绑定。例如:无限滚动场景下可以监听新内容加载到页面,在DOM变化后添加相应的元素或事件。
  2. 表单输入动态验证
    当需要实时校验用户输入内容时,使用MutationObserver来监控表单内容,值的变化以及禁用状态等,即可实现表单的动态校验。
  3. 响应式布局
    当页面布局需要根据DOM变化自适应调整时,使用MutationObserver来监测相关元素的变化,并根据变化动态地调整页面布局。例如,在响应式网页设计中,当窗口大小发生变化或元素被添加或移除时,可以使用MutationObserver来监听相关元素的变化,并根据变化重新计算和调整页面布局,以适应不同的设备和屏幕尺寸。
  4. 组件内部监听
    在自定义组件的开发中,MutationObserver可以用于监听组件内部的DOM变化,以及对应的属性变化。这样可以在组件内部做出相应的处理,如更新组件的状态、重新渲染组件等。例如,当一个自定义组件中的某个子元素被添加或移除时,可以使用MutationObserver来监听这些变化,并在变化发生后更新组件的状态或重新渲染组件。

场景实例

  1. 动态校验表单元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 目标元素(表单)
const form = document.querySelector('#myForm');

// 创建一个 MutationObserver 实例
const observer = new MutationObserver((mutationsList) => {
// 在每次变化时进行处理
for (let mutation of mutationsList) {
// 检查是否是值发生变化的子节点
if (mutation.type === 'childList' && mutation.target.nodeName === 'INPUT') {
// 执行动态校验逻辑
validateForm();
}
// 检查是否是属性变化
if (mutation.type === 'attributes' && mutation.attributeName === 'disabled') {
// 执行动态校验逻辑
validateForm();
}
}
});

// 配置 MutationObserver 监听的类型和目标节点
const config = { childList: true, attributes: true, subtree: true };

// 开始观察表单的变化
observer.observe(form, config);

// 动态校验表单内容的函数
function validateForm() {
// 获取表单元素
const input1 = document.querySelector('#input1');
const input2 = document.querySelector('#input2');

// 获取表单元素的值和禁用状态
const value1 = input1.value;
const value2 = input2.value;
const disabled1 = input1.disabled;
const disabled2 = input2.disabled;

// 执行校验逻辑
// ...
}

  1. 响应式布局
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 创建 MutationObserver 实例
const observer = new MutationObserver(function(mutationsList) {
for (let mutation of mutationsList) {
if (mutation.type === 'childList') {
// 子节点被添加或移除的处理逻辑
adjustLayout();
}
}
});

// 配置 MutationObserver 监听的类型和目标节点
const config = { childList: true, subtree: true };

// 开始观察窗口大小变化和元素添加或移除
window.addEventListener('resize', adjustLayout);
observer.observe(document.body, config);

// 初始化页面布局
adjustLayout();

// 页面布局调整函数
function adjustLayout() {
// 获取窗口宽度
const windowWidth = window.innerWidth;

// 根据窗口宽度调整布局
if (windowWidth > 1024) {
// 大屏幕布局
// ...
} else if (windowWidth > 768) {
// 中屏幕布局
// ...
} else {
// 小屏幕布局
// ...
}
}

参考内容

MDN Web Docs - MutationObserver

注:实例代码均由ChatGpt生成