MutationObserver应用于前端水印

466 阅读2分钟

产品要求做一个页面水印,为了防止水印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`)
});

同时回调函数接收两个参数mutationRecordobserver:

  • mutationRecord: 所有被触发改动的MutationRecord的对象数组
  • observer: 调用该函数的MutationObserver对象。

它会在指定的DOM触发事件时,调用指定的回调函数。MutationObserver 对 DOM 的观察不会立即启动;而必须先调用 observe()方法来确定,要监听哪一部分的 DOM 以及要响应哪些更改。

实例方法

observe() 启动

obs.observe(target, [, options]);

target

DOM树中的一个要观察变化的DOM节点,或者是被观察的的子节点树的根节点。

options

描述DOM哪些变化会回调MutationObservecallback。当调用observe()时,childListattributescharacterData中,必须有一个参数为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();

兼容性

image.png

前端水印

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 });