在项目中有许多的监听事件,这里介绍一个监听元素属性变化的异步回调;可运用在项目中监听某个元素的属性/个别属性变化来执行其异步的回调函数;
应用场景:该元素的属性或显示状态发生改变时,执行异步回调(如埋点或者添加属性行为);
MutationObserver
使用实例
例如这个toPage()
的方法,执行时实例化一个MutationObserver
的构造函数,并监听该id
的style
的属性;当style的属性显示时进行添加埋点;实例代码如下:
toPage(elementId, id) { // 初始化进入XX页面的埋点
const that = this;
const element = document.getElementById(elementId); // 对象
console.log(element, "element")
const observer = new MutationObserver(() => {
// 仅在打开时触发
if (element.style.display !== 'none') {
console.log("打点", element)
// that.reportEvent(id);进行页面弹窗的埋点
}
});
// 述配置开始观察目标节点
observer.observe(element, {
attributes: true, // configure it to listen to attribute changes,
attributeFilter: ['style'],
});
// 之后,可停止观察
//observer.disconnect();
}
MutationObserver
介绍
使用MutationObserver
可以观察整个文档,DOM
树的一部分或某个元素的变化;(此外还可以观察到元素的属性,子节点,文本等的变化),监听element
的元素的变化,可以在dom
被修改时异步的执行回调;
主要应用于:监听某个元素变化时,执行MutationObserver
异步的回调;
- MutationObserver()
创建并返回一个新的 MutationObserver 它会在指定的DOM发生变化时被调用。 - disconnect()
阻止 MutationObserver 实例继续接收的通知,直到再次调用其observe()方法,该观察者对象包含的回调函数都不会再被调用。 - observe()
配置MutationObserver在DOM更改匹配给定选项时,通过其回调函数开始接收通知。
基本的使用
创建一个实例;通过调用MutationObserver构造函数并传入一个回调函数来创建
let observer = new MutationObserver(() => console.log('DOM was mutated!'));
这里新创建的实例不会关联任何的DOM
元素,使用observe()
实现监听;
- observe使用:
observe(监听的dom节点, MutationObserverInit对象)
let observer = new MutationObserver(() => console.log('<body> attributes changed'));
observer.observe(document.body, { attributes: true });
document.body.className = 'foo'; // class属性的更改,会触发observer异步的执行回调
console.log('Changed body class');
// 输出的内容;
// Changed body class
// <body> attributes changed 异步进程
- 回调与
MutationRecord
每次回调都会收录到一个MutationRecord实例的数组,包含的信息发生的变化,以及DOM受到的影响;连续的改变会生成多个MutationRecord实例,回调就会返回包含所有实例的数组:[MutationRecord, MutationRecord, MutationRecord]
let observer = new MutationObserver( (mutationRecords) => console.log(mutationRecords));
observer.observe(document.body, { attributes: true });
document.body.className = 'foo';
document.body.className = 'bar';
document.body.className = 'baz';
// [MutationRecord, MutationRecord, MutationRecord]
- disconnect();终止执行回调,实行“一刀切”的方案,会停止观察所有目标
调用disconent()不仅会停止此后变化的回调,还会抛弃任务队列要执行的回调;
let observer = new MutationObserver(() => console.log('<body> attributes changed'));
observer.observe(document.body, { attributes: true });
document.body.className = 'foo';
observer.disconnect(); // 提前终止服务
document.body.className = 'bar';
//(没有日志输出)
MutationObserverInit
的观察监听的属性值
MutationObserverInit
对象的属性
subtree
; 布尔值 - 是否观察目标节点的子树,默认为false
attributes
; 布尔值 - 观察目标节点属性的变化; 默认false
attributeFilter
;字符串数组 - 观察哪些属性的变化;默认观察所有属性attributeOldValue
; 布尔值 - 表示MutationRecord
是否记录变化之前的属性值characterData
; 布尔值 - 表示修改字符数据是否触发变化事件;默认为false
characterDataOldValue
;布尔值 - 表示MutationRecord
是否记录变化之前的字符数据childList
;布尔值 - 表示修改目标节点的子节点是否触发变化事件;默认为false
异步回调与记录队列
- 记录队列;
每次MutationRecord
被添加到MutationObserver
的记录队列时,仅当之前没有已排期的微
任务回调时(队列中微任务长度为0),才会将观察者注册的回调(在初始化MutationObserver
时传入)作为微任务调度到任务队列上。这样可以保证记录队列的内容不会被回调处理两次。不过在回调的微任务异步执行期间,有可能又会发生更多变化事件。因此被调用的回调会接收到一个MutationRecord
实例的数组,顺序为它们进入记录队列的顺序。回调要负责处理这个数组的每一个实例,因为函数退出之后这些实现就不存在了。回调执行后,这些MutationRecord
就用不着了,因此记录队列会被清空,其内容会被丢弃。 - 调用
MutationObserver
实例的takeRecords()
方法可以清空记录队列,取出并返回其中的所有MutationRecord
实例。看这个例子:
let observer = new MutationObserver( (mutationRecords) => console.log(mutationRecords));
observer.observe(document.body, { attributes: true });
document.body.className = 'foo';
document.body.className = 'bar';
document.body.className = 'baz';
console.log(observer.takeRecords());
console.log(observer.takeRecords());
// [MutationRecord, MutationRecord, MutationRecord]
// []
这在希望断开与观察目标的联系,但又希望处理由于调用 disconnect()
而被抛弃的记录队列中的 MutationRecord
实例时比较有用。