当前位置: 首页 > Web前端 > HTML5

明白HTML5中的MutationObserver

时间:2023-04-05 01:33:33 HTML5

MutationObserver翻译过来就是变化观察者。可以从字面上理解,就是用来观察Node(节点)的变化。MutationObserver在DOM4规范中定义。它的前身是MutationEvent事件。该事件最早在DOM2事件规范中引入,并在DOM3事件规范中正式定义。但是,由于这个事件的兼容性和性能问题,它被放弃了。.MutationEvent虽然MutationEvent已经被弃用了,但我们仍然需要了解它,也许你会因为浏览器兼容性问题(evilbrowsercompatibility)而遇到它。MutationEvent一共有7个事件:DOMNodeInserted、DOMNodeRemoved、DOMSubtreeModified、DOMAttrModified、DOMCharacterDataModified、DOMNodeInsertedIntoDocument、DOMNodeRemovedFromDocument。MutationEvent的兼容性:MutationEvent在IE浏览器中至少支持IE9。在具有webkit内核的浏览器中,IE、Edge和Firefox浏览器不支持DOMAttrModified事件。不支持DOMNodeInsertedIntoDocument和DOMNodeRemovedFromDocument事件。MutationEvent中的所有事件都设计为Itcannotbecancel。如果可以取消MutationEvent事件,则现有的DOM接口不能改变文档,如appendChild、remove等添加和删除节点的DOM操作。MutationEvent中最受诟病的就是性能和安全问题,比如下面这个例子:});所有文档下的DOM添加操作都会触发DOMNodeInserted方法,然后会循环调用DOMNodeInserted方法,导致浏览器崩溃。另外,MutationEvent是一种事件机制,所以一般的事件都会有一个捕获冒泡阶段。这时,如果在捕获和冒泡阶段对DOM进行操作,浏览器的运行速度就会变慢。还有一点就是MutationEvent事件机制是同步的,也就是说每次修改DOM都会触发,而且修改了几次就会触发几次,严重的降低了浏览器的操作,甚至会造成线程在严重的情况下崩溃。

vari=0;block.addEventListener('DOMNodeInserted',function(e){i++});block.appendChild(docuemnt.createTextNode('1'));控制台。日志(i)//1块。appendChild(docuemnt.createTextNode('2'));console.log(i)//2block.appendChild(docuemnt.createTextNode('3'));console.log(i)//3另一个例子:Text
block.addEventListener('DOMNodeInserted',function(e){console.log('1');//1});span.appendChild(docuemnt.createTextNode('其他文本'));在span元素中添加节点会触发块中的DOMNodeInserted事件,但你只想观察块的变化,而不是块中子节点的变化。这时候,你不得不在DOMNodeInserted事件中过滤,忽略对span的操作,这无疑增加了操作的复杂度。MutationObserverMutationObserver的出现就是为了解决MutationEvent带来的问题。首先看一下MutationObserver的浏览器兼容性:我们可以看到IE中MutationObserver至少是IE11。如果你的网站不需要支持IE或者只支持IE11,那么你可以放心使用MutationObserver,否则你可能需要用到上面提到的MutationEvent事件,当然如果你的网站还支持IE8及以下,那么你只能和变异说再见了。MutationObserver是一个构造函数,它接受一个回调参数,回调函数用来处理节点变化,返回两个参数,mutations:节点变化记录列表(sequence),observer:构造一个MutationObserver对象。varobserve=newMutationObserver(function(mutations,observer){})MutationObserver对象有3个方法,分别是:observe:设置观察目标,接受两个参数,target:观察目标,options:设置观察选项通过对象成员disconnect:防止观察者观察到任何变化takeRecords:清除记录队列并返回里面的内容。observe方法中的options参数有几个选项:childList:设置为true表示目标子节点发生变化,比如增加或删除目标子节点,不包括子节点和子节点后代属性的变化:settrue观察目标属性的变化characterData:设置为true观察目标数据的变化subtree:设置为true观察目标及其后代的变化attributeOldValue:如果属性为true或省略,则相当于设置为true,表示需要记录变化前的目标属性值。如果设置了attributeOldValue,则可以省略属性。设置characterDataOldValue:如果characterData为true或省略,相当于设置为true,表示需要记录更改之前的目标数据,设置characterDataOldValue为省略characterDatasetattributeFilter:如果不是所有的属性变化都需要观察,和attributes设置为true或忽略,然后为需要观察的属性设置一个本地名称(不需要命名空间)下表描述了MutationObserver选项和MutationEvent名称之间的对应关系:MutationEventMutationObserveroptionsDOMNodeInserted{childList:true,subtree:true}DOMNodeRemoved{childList:true,subtree:true}DOMSubtreeModified{childList:true,subtree:true}DOMAttriModi{attributes:true,subtree:true}DOMCharacterDataModified{characterData:true,subtree:true}从上表我们也可以看出MutationObserver相比MutationEvent大大增加了灵活性,可以设置各种选项来满足程序员对目标的观察我们简单看几个examples:target的第一个子节点

target的后代

1.callback回调次数vartarget=document.getElementById('target');vari=0varobserve=newMutationObserver(function(mutations,observe){i++});observe.observe(target,{childList:true});target.appendChild(文档.createTextNode('1'));target.appendChild(docuemnt.createTextNode('2'));target.appendChild(docuemnt.createTextNode('3'));console.log(i)//1MutationObserver的callback回调函数为异步,只有在所有DOM操作完成后才会调用回调。2.当只设置{childList:true}时,表示观察目标子节点的变化varobserve=newMutationObserver(function(mutations,observe){debugger;console.log(mutations);//observe.discount();});observe.observe(target,{childList:true});target.appendChild(document.createTextNode('添加文本节点'));//添加节点并观察变化target.childNodes[0].remove();//删除一个节点,可以观察到target.childNodes[0].textContent='改变子节点的后代';//不会被观察到。如果要观察子节点和后代的变化,需要设置{childList:true,thesubtree:true}attributes选项,用于观察目标属性的变化。用法类似childList,对目标属性的增删改查都会被观察到。3、我们需要注意的是选项characterData,用来观察CharacterData类型的节点。只有在节点数据发生变化时才会被观察到。如果删除或添加节点,则不会被观察到,如果不是CharacterData类型的节点变化,则不会被观察到,例如:observe.observe(target,{characterData:true,subtree:true});target.childNodes[0].textContent='改变文本节点';//观察target.childNodes[1].textContent='改变p元素内容';//不会观察target.appendChild(document.createTextNode('NewTextnode'));//不会观察target.childNodes[0].remove();//删除TEXT节点不会被观察到。我们只需要记住,characterData为true的选项只会观察到CharacterData类型节点的数据更改。4.最后要注意一个特别有用的选项attributeFilter。该选项主要用于过滤要观察的属性。例如,如果只想观察目标样式属性的变化,可以这样设置:observe.observe(target,{attributeFilter:['style'],subtree:true});target.style='颜色:红色';//可以被观察到被观察到,当你不想再观察目标节点的变化时,可以调用observe.disconnect()方法取消观察。takeRecords方法用于取出记录队列中的记录。它的一个功能是,比如你不想对一个节点的操作立即做出反应,过一段时间节点的内容就会改变。varobserve=newMutationObserver(function(){});observe.observe(target,{childList:true});target.appendChild(document.createTextNode('新文本节点'));varrecord=observe.takeRecords();//此时record保存的是变化记录列表//当调用takeRecords方法时,会清空记录队列,所以不会触发MutationObserver中的callback回调方法。target.appendChild(document.createElement('span'));观察.断开连接();//停止观察目标。//MutationObserver中的回调函数只有一条记录,只记录了新添加的span元素//之后可以对记录进行操作//...MutationRecord变化记录中的属性如下:type:如果是是属性变化,返回“attributes”,如果一个CharacterData节点(Text节点,Comment节点)发生变化,返回“characterData”,节点树变化返回“childList”返回添加或删除的节点的前一个兄弟节点分别,否则返回nullnextSibling:返回分别添加或删除的节点的下一个兄弟节点,否则返回nullattributeName:返回改变属性的本地名称,否则返回nullattributeNamespace:返回改变的属性命名空间,否则返回nulloldValue:返回值取决于类型。对于“属性”,它是更改前属性的值。对于“characterData”,为变化前节点的数据。对于“childList”,它为空。type和target这两个属性不管用什么观察方式都会有一个返回值。其他属性的返回值与观察方法有关。例如,oldValue仅在attributeOldValue或characterDataOldValue为真时返回,只有当属性改变时,attributeName才有返回值等。