监视DOM元素变动:MutationObserver
MutationObserver
MutationObserver:变动观察器,监视DOM元素的所有变动,包括节点的增减、属性的变动、文本内容的变动;
注:类似于事件,做了一个对应操作,触发对应事件的执行,但是它们有着根本之间的区别:事件的执行时同步的,触发的同时,每个事件依次执行,但是observer是异步触发的,等到所有dom操作结束,再执行。
MutationEvent
MutationObserver是在DOM4规范中定义的,它的前身是MutationEvent事件,该事件最初在DOM2事件规范中介绍,到来了DOM3事件规范中正式定义,但是由于该事件存在兼容性以及性能上的问题被弃用。
MutationEvent总共有7种事件:DOMNodeInserted、DOMNodeRemoved、DOMSubtreeModified、DOMAttrModified、DOMCharacterDataModified、DOMNodeInsertedIntoDocument、DOMNodeRemovedFromDocument
MutationEvent的兼容性:
- MutationEvent在IE浏览器中最低支持到IE9;
- 在webkit内核的浏览器中,不支持 DOMAttrModified 事件;
- IE,Edge以及Firefox浏览器下不支持 DOMNodeInsertedIntoDocument 和 DOMNodeRemovedFromDocument事件;
缺点:
1,MutationEvent中的所有事件都被设计成无法取消,如果可以取消MutationEvent事件则会导致现有的DOM接口无法对文档进行改变,比如appendChild,remove等添加和删除节点的DOM操作。
2,document下的所有DOM添加操作都会触发DOMNodeInserted方法,这时就会出现循环调用DOMNodeInserted方法,导致浏览器崩溃
3,还有就是MutationEvent是事件机制,因此会有一般事件都存在的捕获和冒泡阶段,此时如果在捕获和冒泡阶段又对DOM进行了操作会拖慢浏览器的运行。
4,MutationEvent事件机制是同步的,也就是说每次DOM修改就会触发,修改几次就触发几次,严重降低浏览器的运行,严重时甚至导致线程崩溃
目的:
应付 DOM 频繁的变动
特点:
- 异步执行,它等待所有脚本任务完成后,才会运行;
- 它把 DOM 变动记录封装成一个数组进行处理,而不是一条条个别处理 DOM 变动;
- 它既可以观察 DOM 的所有类型变动,也可以指定只观察某一类变动;
observe(DOM, options)
1, DOM 为要监听的节点
2, options 可以配置要监听的具体内容
3, 可以调用多次 observe ,同时监听多个 DOM 的变动。
{
attributeFilter: ['data-name'], // 要监听的特定属性数组
attributeOldValue: true, // MutationRecords 中是否记录属性变动的旧值
attributes: true, // 是否监听属性变化
characterData: true, // 是否监听字符变化
characterDataOldValue: true, // MutationRecords 中是否记录字符串变动的旧值
childList: true, // 是否监听子字节点的新增删除
subtree: true, // 监听范围扩展为节点树中所有的节点,否则只监听子节点的变动
}
disconnct()
取消当前的监听,取消之后又可以通过 observe 再次监听
takeRecords()
callback 的触发是异步的, 可能出现虽然监测到了变动记录,但是还没有调用 callback 处理, takeRecords 可以取出当前所有未处理的 MutationRcords 。一般情况用在要调用 disconnect 之前,一次取出所有的记录,并且处理。
MutationRecord
MutationRecord 是一次变动的记录;
MutationRecord {
type, // 变动类型,值有 attributes(属性变动),characterData(字符变动),childList(子节点删除,新增)
target, // 变动影响的 Node, 如果 type = attributes, 则是 属性变动的 Node
// 如果 type = characterData, 则是 字符修改的 TextNode
// 如果 type = childList, 则是 父节点
addedNodes, // 增加的节点,如果没有则为空数组
removedNodes, // 删除的节点,如果没有则为空数组
previousSibling, // 删除/增加节点的 前一个节点
nextSibling, // 删除/增加节点的 前一个节点
attributeName, // 变动的属性名
attributeNamespace, // 变动属性的namespace
oldValue, // 变动的老的值 如果 type = attributes, 则是 属性修改前的值
// 如果 type = characterData, 则是 字符修改前的值
// 如果 type = childList, 则是 null
}
优点
很方便的监听节点所有或特定的变化
缺点
回调的触发时异步的。click 等事件触发是同步的。
应用
实际场景:使用vant 中fileld组件的多文本框属性,进入页面时隐藏,点击按钮触发显示,此时多行文本框高度只有一行且超出有滑条,而不会自适应高度。
解决方案:显示多行文本框时刷新页面
// 观察dom对象变动
export function observeDom( el, cb, options = {
attributes: true,
subtree: true,
attributeOldValue: true
}) {
const observer = new MutationObserver(cb);
observer.observe(el, options);
}
// 监控变动的dom
observeDom(this.$refs.moreDom, () => {
this.$refs[i].adjustSize(); // adjustSize()方法是由vant提供的自适应高度方法
}, {
attributes: true,
subtree: false,
attributeOldValue: true
});