产品要求做一个页面水印,为了防止水印DOM节点被删除或属性被修改,所以需要用到MutationObserver
MutationObserver
概念
Mutation API用来监听DOM的变动。例如DOM的新增、删除、属性变动、文本内容变动、位置变动等,都能触发这个API的回调。MutationObserver接口是出于性能考虑而设计的,其核心是异步回调与记录队列模型,目的是为了在大量变化事件发生时不影响性能,每次变化的信息会保存到MutaionRecord实例中,然后添加到记录队列。这个队列对每个MutaionObserver实例都是唯一的,是所有DOM变化事件的有序列表。
构造
MutationObserver需要通过MutationObserver构造函数并传入一个回调函数来创建:
let obs = new MutationObserver((mutationRecord, observer) => {
console.log(mutaion)
console.log(`DOM change`)
});
同时回调函数接收两个参数mutationRecord和observer:
mutationRecord: 所有被触发改动的MutationRecord的对象数组observer: 调用该函数的MutationObserver对象。
它会在指定的DOM触发事件时,调用指定的回调函数。MutationObserver 对 DOM 的观察不会立即启动;而必须先调用 observe()方法来确定,要监听哪一部分的 DOM 以及要响应哪些更改。
实例方法
observe() 启动
obs.observe(target, [, options]);
target
DOM树中的一个要观察变化的DOM节点,或者是被观察的的子节点树的根节点。
options
描述DOM哪些变化会回调MutationObserve的callback。当调用observe()时,childList、attributes和characterData中,必须有一个参数为true。否则会抛出TypeError异常。
| 属性名 | 是否必填 | 描述 |
|---|---|---|
| subtree | 否 | 当为true时,将会监听整个target下的所有子树,以及子树中的所有节点的属性。 |
| childList | 否 | 当为true时,监听target节点中发生节点的新增、删除 |
| attributes | 否 | 当为 true 时观察所有监听的节点属性值的变化 |
| attributeFilter | 否 | 一个用于声明哪些属性名会被监听的数组 |
| attributeOldValue | 否 | 当为 true 时,记录上一次被监听的节点的属性变化 |
| characterData | 否 | 当为 true 时,监听声明的 target 节点上所有字符的变化 |
| characterDataOldValue | 否 | 当为 true 时,记录前一个被监听的节点中发生的文本变化 |
disconnect() 断开链接
一般,只要观察的元素不被垃圾回收,MutationObserver的回调就会响应DOM变化事件,从而被执行。要提前终止执行回调,可以调用disconnect()。
obs.disconnect();
注意: 这里调用disconnect后,不仅会停止此后变化事件的回调,也会抛弃已经加入任务队列要异步执行的回调。
takeRecords()
调用MutationObserver实例的takeRecords()方法可以清空记录队列,取出并返回其中的所有 MutationRecord实例。
obs.takeRecords();
兼容性
前端水印
const createWaterMark = () => {
const dom = document.createElement('div');
dom.id = 'waterMark';
for (let i = 0; i < 3; i++) {
const text = document.createElement('p');
text.className = 'text';
text.textContent = 'Hello World!'
dom.appendChild(text);
}
document.body.appendChild(dom);
}
createWaterMark();
const observer = new MutationObserver((mutations) => {
const targetDom = document.getElementById('waterMark')
const [mutation] = mutations
if (mutation.target === document.body && mutation.removedNodes[0]?.id === 'waterMark') {
targetDom && document.body.removeChild(targetDom);
createWaterMark()
}
if (mutation.target === targetDom) {
targetDom && document.body.removeChild(targetDom);
createWaterMark()
}
});
observer.observe(document.body, { subtree: true, attributes: true, childList: true });