使用 MutationObserver 跟踪 DOM 的变化


=Start=

缘由:

真的是隔行如隔山,虽然都属于IT行业,但是我对于前端的很多东西都不了解(尤其是近些年前端的发展变化日新月异),好后悔之前没有好好学习前端的知识……

这里简单记录一下前段时间了解到的MutationObserver这个知识点,方便以后要用的时候参考。

正文:

参考解答:
一、DOM是什么?

当网页被加载时,浏览器会创建页面的文档对象模型(DOM, Document Object Model)。通过 HTML DOM,可访问 JavaScript HTML 文档的所有元素。通过可编程的对象模型,JavaScript 获得了足够的能力来创建动态的 HTML。

二、如何监听DOM的变化?
  • 轮询(Polling)
  • MutationEvents
  • CSS animations
  • MutationObserver
三、MutationObserver的方案

Mutation Observer API 用来监视 DOM 变动。DOM 的任何变动,比如节点的增减、属性的变动、文本内容的变动,这个 API 都可以得到通知。

概念上,它很接近事件,可以理解为 DOM 发生变动就会触发 Mutation Observer 事件。但是,它与事件有一个本质不同:事件是同步触发,也就是说,DOM 的变动立刻会触发相应的事件;Mutation Observer 则是异步触发,DOM 的变动并不会马上触发,而是要等到当前所有 DOM 操作都结束才触发。

这样设计是为了应付 DOM 变动频繁的特点。举例来说,如果文档中连续插入1000个 元素,就会连续触发1000个插入事件,执行每个事件的回调函数,这很可能造成浏览器的卡顿;而 Mutation Observer 完全不同,只在 1000 个段落都插入结束后才会触发,而且只触发一次。

Mutation Observer有以下特点:

  1. 它等待所有脚本任务完成后,才会运行,即采用异步方式
  2. 把 DOM 变动记录封装成一个数组进行处理,而不是一条条地个别处理 DOM 变动
  3. 既可以观察发生在 DOM 节点的所有变动,也可以观察某一类变动
四、一个简单的样例
<!DOCTYPE html>
<html>
<head>
	<title>MutationObserver-test</title>
	<script type="text/javascript">
// 选择需要观察变动的节点
var targetNode = document.getElementById('uniq_id_name');

// 观察器的配置(需要观察什么变动)
const config = { attributes: true, childList: true, subtree: true };

// 当观察到变动时执行的回调函数
const callback = function(mutationsList, observer) {
    // Use traditional 'for loops' for IE 11
    for(let mutation of mutationsList) {
        if (mutation.type === 'childList') {
            console.log('A child node has been added or removed.');
        }
        else if (mutation.type === 'attributes') {
            console.log('The ' + mutation.attributeName + ' attribute was modified.');
        }
        console.log(mutation);
    }
};

// 创建一个观察器实例并传入回调函数
const observer = new MutationObserver(callback);

// 以上述配置开始观察目标节点
// observer.observe(targetNode, config); /* Uncaught TypeError: Failed to execute 'observe' on 'MutationObserver': parameter 1 is not of type 'Node'. */
observer.observe(document, config); /* 第一个参数试了很多,发现只能是 document 才不会报错…… */

// 之后,可停止观察
// observer.disconnect();
	</script>
</head>

<body>
	<div id="uniq_id_name">
		this is original div's content.
	</div>
</body>

</html>

五、MutationObserver的实际使用场景
  • 你希望通知 Web 应用程序访问者,他当前所在的页面发生了一些更改。
  • 你正在开发一个新的 JavaScript 框架,需要根据 DOM 的变化动态加载 JavaScript 模块。
  • 也许你正在开发一个所见即所得(WYSIWYG) 编辑器,试图实现撤消/重做功能。通过利用 MutationObserver API,你可以知道在任何给定的点上进行了哪些更改,因此可以轻松地撤消这些更改。
  • 还有给页面添加水印的时候,防止一些不怀好意的人对水印的元素做手脚来修改或隐藏水印。
参考链接:

JavaScript HTML DOM
https://www.runoob.com/js/js-htmldom.html

https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver

https://wangdoc.com/javascript/dom/mutationobserver.html

JavaScript是如何工作的:使用MutationObserver跟踪DOM的变化
https://blog.fundebug.com/2019/01/10/understand-mutationobserver/
https://blog.sessionstack.com/how-javascript-works-tracking-changes-in-the-dom-using-mutationobserver-86adc7446401

Mutation Observer 的三个实际应用
https://www.zcfy.cc/article/three-real-world-uses-for-mutation-observer
https://eager.io/blog/three-real-world-use-cases-for-mutation-observer/

canvas 生成页面水印,MutationObserver 控制节点防修改
https://www.liangzl.com/get-article-detail-134311.html

制作水印
https://www.dazhuanlan.com/2020/03/15/5e6d69a58de32/
https://www.cnblogs.com/jscode/p/3600060.html

前端水印初探
https://www.mdeditor.tw/pl/2RBr

页面水印插件开发
http://www.wuhub.top/the-development-of-page-watermark/

https://stackoverflow.com/questions/tagged/mutation-observers?tab=Votes

Detect changes in the DOM
https://stackoverflow.com/questions/3219758/detect-changes-in-the-dom

=END=


《 “使用 MutationObserver 跟踪 DOM 的变化” 》 有 2 条评论

  1. 阿里巴巴公司根据截图查到泄露信息的具体员工的技术是什么?
    https://www.zhihu.com/question/50735753

    阿里巴巴内网的不可见水印用的是什么算法?
    https://www.zhihu.com/question/50677827/answer/122239863
    `
    在阿里实习的时候,分析过这个东西,当然只是纯好奇技术啦。对外公开内网的信息本身就是不道德的,而且可能触犯保密协议。

    首先,第一道防线。看截图这个人是用阿里内外的iOS客户端截的图,且不说截下来的图 PS 能不能完整去水印。在截图操作的瞬间,客户端首先就能感知到你有没有截图。 iOS 有这个 API,Android 也能通过监听截图保存路径实现 ,做过移动端开发的人应该都有所了解。所以你在什么时间、什么页面截了图,其实都能记录下来并上传服务器的。可参考:
    RxScreenshotDetector:Android 截屏检测
    iOS开发-检测用户截屏, 并获取所截图片

    再者,第二道防线。在手机客户端截图是不方便去水印的。水印其实就是写着工号的透明度很高的图片,background-image 的方式平铺满整个屏幕。如果在 chrome里,直接审查元素去掉就行,这样就去除干净了。如果先截了图,再用 PS 去除的话,就有点麻烦了。因为用 PS 去除的话很容易漏掉一些东西,比如工号二进制。

    (很多人不理解水印的原理,这里多解释几句,这种高透明度的水印从正面看屏幕的话肉眼很难看到。从屏幕上下斜看,也能隐约看到一些。在 PS 里调下色阶马上就显现出来了。

    第三道防线,很多人知道这个水印里有工号,但不知道还有工号的二进制。就是用.和/组成的二进制,和工号放在一张图片里。工号的水印调调对比度、色阶,甚至斜着看屏幕就能看到,但是“./”组成的二进制又小又隐蔽,所以用 PS 的话很容易漏掉。

    最后一道防线,还记得之前阿里 HR 伪造员工离职谈话的那个风波的时候。即便一个懂前端的程序员去除了水印,最后还是被查出来了,这是为什么?个人推测,可能是根据截图中的评论数、芝麻数等等随着时间变化的数据,反推出截图的时间区间,然后根据时间筛选访问这个帖子的访问日志,缩小范围到几个人,再逐个约谈,大概就能确定了。

    很多知友说通过背景色差、文字段落间距、排版什么的就有点太高端了,阿里目前应该不会用到吧……

    至于中间人监控员工上网信息,这个不了解还是别不推测了……
    `

  2. 探秘 Web 水印技术
    https://mp.weixin.qq.com/s/bXpDxN2tTS9FvVQMqLGB2w
    `
    Web 水印技术在信息安全和版权保护等领域有着广泛的应用,对防止信息泄露或知识产品被侵犯有重要意义。水印根据可见性可分为可见水印和不可见水印(盲水印),本文将分别予以介绍,带你探秘 web 水印技术。

    # 可见水印
    * 最简单的水印——一种比较常见的简单水印场景是给文章、表格加上 logo 水印,用以申明版权。CSS声明很简单 background-image:url(“./logo.png”);
    * 全页面水印——为了避免被其他元素遮挡,针对页面的水印一般会使用一个层级比较高且覆盖整个页面的元素来承载。一个重要的 CSS 声明会使该元素“可穿透”,“看得见、摸不着”,不再影响页面操作 pointer-events: none;
    * 动态水印——每个用户的水印内容是不同的,无法通过提前准备好一张图片来满足。
    ** 服务端方案——优点是兼容性好,缺点是需要前后端配合,增加了页面请求和服务端资源开销,防攻击能力也较差。
    ** Canvas 方案——这种方式不需要服务端配合,在前端就可以完成,且有助于减少请求和服务端资源开销。曾经面临的浏览器兼容问题现在也不再是问题,该方案已逐渐流行起来。
    ** SVG 方案——一般用于纯文字的水印

    # 水印安全
    Shadow DOM – 为了提高 web 水印的隐蔽性,同时避免受外部代码影响,从而在一定程度上防止篡改,可以考虑把水印元素放在 Shadow DOM 中。
    Mutation Observer – Shadow DOM 提高了水印的隐蔽性,同时可以防止外部代码修改。除此之外,还有一种常见的攻击场景——人为修改,比如在浏览器控制台直接修改或删除对应的 DOM 元素。

    # 不可见水印(盲水印)
    理论上的优势:
    * 更好的观感。可见水印总给人一种“膏药感”,甚至会引起部分人的不适,而不可见水印则不会有这个问题。
    * 更佳的隐蔽性。用户基本感知不到水印的存在。
    * 更强的抗攻击性。可见水印更容易受到攻击,而不可见水印除了隐蔽性比较强之外,其自身往往还具备比较强的抗攻击能力。

    # Web 中的数字水印应用
    上面介绍了几种常见的不可见水印(盲水印)实现方式,其中鲁棒性比较好的是基于频域的数字图像盲水印,但这种水印主要是针对数字图像,而 Web 上的内容载体并不一定都是图片,常见的需要加水印的载体除了图片还有文本、表格等,这些场景该如何应用频域盲水印呢?

    或许,Canvas 就是答案。
    `

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注