MutationObserver翻译过来就是变更观察器,字面上就能够理解这是用来观察Node(节点)变化的。MutationObserver是在DOM4规范中定义的,它的前身是MutationEvent事件,该事件最初在DOM2事件规范中介绍,到来了DOM3事件规范中正式定义,可是因为该事件存在兼容性以及性能上的问题被弃用。程序员
虽然MutationEvent已经被弃用,可是咱们仍是须要了解它,可能你会为了浏览器兼容性的问题而遇到它(万恶的浏览器兼容性)。web
MutationEvent总共有7种事件:DOMNodeInserted、DOMNodeRemoved、DOMSubtreeModified、DOMAttrModified、
DOMCharacterDataModified、DOMNodeInsertedIntoDocument和DOMNodeRemovedFromDocument。浏览器
MutationEvent的兼容性:安全
MutationEvent中的全部事件都被设计成没法取消,若是能够取消MutationEvent事件则会致使现有的DOM接口没法对文档进行改变,好比appendChild,remove等添加和删除节点的DOM操做。
MutationEvent中最使人诟病的就是性能以及安全性的问题,好比下面这个例子:app
document.addEventListener('DOMNodeInserted', function() { var newEl = document.createElement('div'); document.body.appendChild(newEl); });
document下的全部DOM添加操做都会触发DOMNodeInserted方法,这时就会出现循环调用DOMNodeInserted方法,致使浏览器崩溃。还有就是MutationEvent是事件机制,所以会有通常事件都存在的捕获和冒泡阶段,此时若是在捕获和冒泡阶段又对DOM进行了操做会拖慢浏览器的运行。异步
另外一点就是MutationEvent事件机制是同步的,也就是说每次DOM修改就会触发,修改几回就触发几回,严重下降浏览器的运行,严重时甚至致使线程崩溃函数
<div id='block'></div>
var i=0; block.addEventListener('DOMNodeInserted', function(e) { i++ }); block.appendChild(docuemnt.createTextNode('1')); console.log(i) //1 block.appendChild(docuemnt.createTextNode('2')); console.log(i) //2 block.appendChild(docuemnt.createTextNode('3')); console.log(i) //3
再看个例子:性能
<div id='block'> <span id='span'>Text</span> </div>
block.addEventListener('DOMNodeInserted', function(e) { console.log('1'); //1 }); span.appendChild(docuemnt.createTextNode('other Text'));
span元素中添加节点会触发block中的DOMNodeInserted事件,但是你只想观察block的变化,不想观察block中子节点的变化,这时你不得不在DOMNodeInserted事件中进行过滤,把对span的操做忽略掉,这无疑增长了操做的复杂性。网站
MutationObserver的出现就是为了解决MutationEvent带来的问题。
先看一下MutationObserver的浏览器兼容性:spa
咱们能够看到MutationObserver在IE中最低要就是IE11,若是你的网站不须要支持IE或者只支持到IE11,那么你能够放心的使用MutationObserver,不然你可能须要用到上面提到的MutationEvent事件,固然若是你的网站还要支持IE8及如下版本,那么你只能和Mutation说拜拜了。
MutationObserver是一个构造器,接受一个callback参数,用来处理节点变化的回调函数,返回两个参数,mutations:节点变化记录列表(sequence<MutationRecord>),observer:构造MutationObserver对象。
var observe = new MutationObserver(function(mutations,observer){ })
MutationObserver对象有三个方法,分别以下:
关于observe方法中options参数有已下几个选项:
下表描述了MutationObserver选项与MutationEvent名称之间的对应关系:
MutationEvent | MutationObserver options |
---|---|
DOMNodeInserted | { childList: true, subtree: true } |
DOMNodeRemoved | { childList: true, subtree: true } |
DOMSubtreeModified | { childList: true, subtree: true } |
DOMAttrModified | { attributes: true, subtree: true } |
DOMCharacterDataModified | { characterData: true, subtree: true } |
从上表咱们也能够看出相比与MutationEvent而言MutationObserver极大地增长了灵活性,能够设置各类各样的选项来知足程序员对目标的观察。
咱们简单看几个例子:
<div id='target' class='block' name='target'> target的第一个子节点 <p> <span>target的后代</span> </p> </div>
1.callback的回调次数
var target=document.getElementById('target'); var i=0 var observe=new MutationObserver(function (mutations,observe) { i++ }); observe.observe(target,{ childList: true}); target.appendChild(docuemnt.createTextNode('1')); target.appendChild(docuemnt.createTextNode('2')); target.appendChild(docuemnt.createTextNode('3')); console.log(i) //1
MutationObserver的callback回调函数是异步的,只有在所有DOM操做完成以后才会调用callback。
2.当只设置{ childList: true}时,表示观察目标子节点的变化
var observe=new MutationObserver(function (mutations,observe) { debugger; console.log(mutations); //observe.discount(); }); observe.observe(target,{ childList: true}); target.appendChild(document.createTextNode('新增Text节点')); //增长节点,观察到变化 target.childNodes[0].remove(); //删除节点,能够观察到 target.childNodes[0].textContent='改变子节点的后代'; //不会观察到
若是想要观察到子节点以及后代的变化需设置{childList: true, subtree: true}
attributes选项用来观察目标属性的变化,用法相似与childList,目标属性的删除添加以及修改都会被观察到。
3.咱们须要注意的是characterData这个选项,它是用来观察CharacterData类型的节点的,只有在改变节点数据时才会观察到,若是你删除或者增长节点都不会进行观察,还有若是对不是CharacterData类型的节点的改变不会观察到,好比:
observe.observe(target,{ characterData: true, subtree: true}); target.childNodes[0].textContent='改变Text节点'; //观察到 target.childNodes[1].textContent='改变p元素内容'; //不会观察到 target.appendChild(document.createTextNode('新增Text节点')); //不会观察到 target.childNodes[0].remove(); //删除TEXT节点也不会观察到
咱们只须要记住只有对CharacterData类型的节点的数据改变才会被characterData为true的选项所观察到。
4.最后关注一个特别有用的选项attributeFilter,这个选项主要是用来筛选要观察的属性,好比你只想观察目标style属性的变化,这时能够以下设置:
observe.observe(target,{ attributeFilter: ['style'], subtree: true}); target.style='color:red'; //能够观察到 target.removeAttribute('name'); //删除name属性,没法观察到
disconnect方法是用来阻止观察的,当你再也不想观察目标节点的变化时能够调用observe.disconnect()方法来取消观察。
takeRecords方法是用来取出记录队列中的记录。它的一个做用是,好比你对一个节点的操做你不想立刻就作出反应,过段时间在显示改变了节点的内容。
var observe=new MutationObserver(function(){}); observe.observe(target,{ childList: true}); target.appendChild(document.createTextNode('新增Text节点')); var record = observe.takeRecords(); //此时record保存了改变记录列表 //当调用takeRecords方法时,记录队列被清空所以不会触发MutationObserver中的callback回调方法。 target.appendChild(document.createElement('span')); observe.disconnect(); //中止对target的观察。 //MutationObserver中的回调函数只有一个记录,只记录了新增span元素 //以后能够对record进行操做 //...
MutationRecord
变更记录中的属性以下:
其中 type、target这两个属性不论是哪一种观察方式都会有返回值,其余属性返回值与观察方式有关,好比只有当attributeOldValue或者characterDataOldValue为true时oldValue才有返回值,只有改变属性时,attributeName才有返回值等。