如何使用js监听特定div内容的变化

最近在开发DAdolfans的时候遇到了这么个需求:监听一个contentEditablediv内容变化。

我们知道,监听textarea或者input内容的变化通过简单的jq监听就能做到,但是如果监听的对象是一个div的话它们就无能为力了。上网查了几个方法:

先说个比较丧病的处理方法,监听click keyup blur三个事件,如果触发了这三个事件就视作内容发生了变化,这个方法的缺点非常明显,你按下ctrl都会算做内容变化了。看到这简单粗暴的处理方法当时的我都被吓尿了٩(ŏ﹏ŏ、)۶,不过还是乖乖的用了这货……

第二种方法是把div的内容扔一个变量里,用setsetInterval高频扫描。唔,其实这样也是一种解决方法啦……不过百万字级别的文档这么扫明显还是比较吃资源的_(¦3」∠)_

第三种方法是DOMNodeInserted事件,当DOM有插入节点的行为时触发,不过……Σ(゚∀゚ノ)ノ 麻麻这个会漏判好多东西好嘛!如果我不插入新的Node而是编辑一个现有的Node它不会有反应啊:(´ཀ`」 ∠): 。

之后,昨天,在知乎上一篇毫不相干的文章中看到了这么个玩意,叫MutationObserver,MDN(文章末尾有地址)上对它的定义是这么说的:

MutationObserver给开发者们提供了一种能在某个范围内的DOM树发生变化时作出适当反应的能力.该API设计用来替换掉在DOM3事件规范中引入的Mutation事件.

看了下简介,新引入的东西,兼容性肯定不好,不过本着旧版本浏览器干我P事的原则我在最新版本的各个浏览器试了一下d(・∀・)b,window.MutationObserver`均可用。

这里强调一下所谓的可用是不需要引用私有方法,MDN上的代码已经过于陈旧,我说的是这行:

1
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver

最新的版本已经不需要后面的两个了,在火狐下定义一个Moz开头的会直接报Undefined,Webkit(其实是Blink)下前两个都能返回正确的结果。所以去参考MDN的孩子们注意一下这里。

然后说一下用法,MDN说的非常明白,这里就简单复述下在前面提到的需求:监听一个contentEditablediv内容变化,这货怎么写。

选择一个要去监控的对象:

1
mainArea = document.querySelector("#main_area");

querySelector是个新玩意,是JS原生的内容,做到像jQuery选择器一样,用类似css的选择方式去选择元素;当听说这玩意出现的时候我流下了两行热泪(;´ ༎ຶ Д ༎ຶ`)σ selectElementById再见……

之后实例化一个MutationObserver

1
2
3
4
MutationObserver = window.MutationObserver
DocumentObserver = new MutationObserver(function() {
//BALABALABALA
});

之后需要告诉这个观察者你需要观察的内容:

1
2
3
4
5
6
DocumentObserverConfig = {
attributes: true,
childList: true,
characterData: true,
subtree: true
};

监听的内容包括divattr发生变化、子节点发生了变化、文本节点发生了变化、子节点的后代发生变化。

当上述变化发生时激活回调函数。

接下来开始监听:

1
DocumentObserver.observe(mainArea, DocumentObserverConfig);

这样它就会开始监听了。

不过其实这个函数并不是拿来干这个的……它是用来追踪元素内容变化过程的,所以队列里会积累一些东西。为了节省资源我们可以定义一个释放方法:

1
2
3
4
function refreshObserver() {
DocumentObserver.disconnect();
DocumentObserver.observe(mainArea, DocumentObserverConfig);
}

之后适当的时间释放一下就好了。

没了(∫・ω・)∫

参考资源:https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver