MutationObserver 是 JavaScript 中监听 DOM 变化的最高效标准方式,能精准捕获子节点增删、属性变更、文本变化等,异步批量触发且性能友好;需传入回调函数接收变化记录列表和观察器实例,并通过配置对象指定监听类型如 childList、subtree、attributes 等。

MutationObserver 是 JavaScript 中监听 DOM 变化最高效、标准的方式,比旧的 DOMNodeInserted 或轮询更可靠。它能精准捕获子节点增删、属性变更、文本内容变化等,且是异步批量触发,性能友好。
创建并配置 MutationObserver 实例
需传入一个回调函数,该函数在 DOM 变化后异步执行,接收两个参数:变化记录列表(MutationRecord[])和观察器实例本身。
配置对象决定监听哪些变化类型,常用选项包括:
-
childList:设为
true才能监听子节点的添加、删除 -
subtree:设为
true可递归监听后代节点(含深层嵌套) -
attributes:设为
true监听元素属性变化(如class、src) -
attributeFilter:可选数组,限定只监听指定属性名(如
['class', 'data-id']) -
characterData:设为
true监听文本节点内容变化(较少用)
监听子节点增删的典型用法
适用于动态插入广告、评论、卡片等场景。例如监听某个容器内新增了哪些元素:
立即学习“Java免费学习笔记(深入)”;
const container = document.getElementById('list');
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
if (node.nodeType === 1) { // 只处理元素节点
console.log('新增元素:', node);
// 可在此初始化新节点上的组件或事件绑定
}
});
mutation.removedNodes.forEach(node => {
if (node.nodeType === 1) {
console.log('移除元素:', node);
}
});
}
});
});
observer.observe(container, {
childList: true,
subtree: true // 确保监听所有后代层级的增删
});
监听属性变化并获取新旧值
当需要响应 class 切换、状态属性更新(如 data-loading)时,启用 attributes: true 并开启 attributeOldValue: true 才能拿到变更前的值:
const target = document.querySelector('.status-btn');
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
if (mutation.type === 'attributes') {
const attrName = mutation.attributeName;
const oldValue = mutation.oldValue;
const newValue = target.getAttribute(attrName);
console.log(`属性 ${attrName} 从 "${oldValue}" 变为 "${newValue}"`);
}
});
});
observer.observe(target, {
attributes: true,
attributeOldValue: true, // 关键:不加这行 oldvalue 始终为 undefined
attributeFilter: ['class', 'data-status'] // 只关注这两个属性
});
注意事项与最佳实践
- 观察器默认不会自动停止,记得在不需要时调用
observer.disconnect()避免内存泄漏(比如组件卸载时) - 同一节点上多次
observe()不会报错,但会覆盖前一次配置;如需叠加监听,应先disconnect()再重新配置 - MutationRecord 的
target是发生变化的节点,不是你传入的根节点;mutation.target可能是子元素 - 避免在回调中直接修改被监听的 DOM,否则可能触发新一轮观察,造成无限循环(必要时可用
setTimeout延迟操作)


















