理解 Mutation Observer
2017-05-13 19:07 龙恩0707 阅读(3779) 评论(0) 编辑 收藏 举报阅读目录
1.理解 Mutation Observer
Mutation Observer(变动观察器) 是监听DOM变动的接口,DOM发生任何变动,Mutation Observer会得到通知。
它与事件类似,但有所不同,事件是同步的,也就是说DOM发生变动,事件立刻会处理,而Mutation Observer则是异步,它不会立即处理,而是等页面上所有的DOM完成后,执行一次,如果页面上要操作100次DOM的话,那么事件会监听100次DOM,而Mutation Observer只会执行一次,等所有的DOM操作完成后,执行。
它的特点是:
1. 等待所有脚本任务完成后,才会执行,即采用异步方式。
2. DOM的变动记录封装成一个数组进行处理,而不是一条条的个别处理DOM变动。
3. 还可以观测发生在DOM的所有类型变动,也可以观测某一类变动。
浏览器支持如下:
下面的代码是检测浏览器是否支持该属性。如下代码:
var MutationObserver = window.MutationObserver || window.WebkitMutationObserver || window.MozMutationObserver; // 监测浏览器是否支持 var observeMutationSupport = !!MutationObserver;
MutationObserver 构造函数
使用 MutationObserver 构造函数,新建一个观察器实例,同时指定该实例的回调函数。如下:
var observer = new MutationObserver(callback);
观察器回调函数会在每次DOM发生变动后调用,接受2个参数,第一个是变动数组,第二个是观察器实例。
Mutation Observer 实例的方法
1. observe() 该方法所要观察的DOM节点,以及所要观察的特定变动。
该方法接受2个参数,第一个参数是所要观察的DOM元素,第二个所要观察的变动类型。
调用方式:observer.observe(dom, options);
那么类型有如下:
childList: 子节点的变动。
attributes: 属性的变动。
characterData: 节点内容或节点文本的变动。
subtree 所有后代节点的变动。
需要观察哪一种变动类型,需要在options对象中指定为true即可;但是如果设置subtree的变动,必须同时指定childList, attributes 和 characterData 中的一种或多种。
1. 监测childList的变动;
如下demo代码:
<!DOCTYPE html> <html> <head> <title>演示Vue</title> <style> </style> </head> <body> <div id='demo'> <ol> <li>111111</li> </ol> </div> </body> <script type="text/javascript"> var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; var list = document.querySelector('ol'); var Observer = new MutationObserver(function(mutations, instance){ console.log(mutations); console.log(instance); mutations.forEach(function(mutation){ console.log(mutation); }); }); Observer.observe(list, { childList: true, subtree: true }); list.appendChild(document.createElement('div')); list.appendChild(document.createTextNode('foo')); list.removeChild(list.childNodes[0]); list.childNodes[0].appendChild(document.createElement('div')); </script> </html>
如上代码 在控制台上打印信息如下,我们打印第一个回调参数 mutations 后,截图如下:
2. 监测characterData的变动
代码如下:
<!DOCTYPE html> <html> <head> <title>演示Vue</title> <style> </style> </head> <body> <div id='demo'> <ol> <li>111111</li> </ol> </div> </body> <script type="text/javascript"> var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; var list = document.querySelector('ol'); var Observer = new MutationObserver(function(mutations, instance){ mutations.forEach(function(mutation){ console.log(mutation); }); }); Observer.observe(list, { childList:true, characterData:true, subtree:true }); list.childNodes[0].data = "cha"; </script> </html>
3. 监测属性的变动;
代码如下:
<!DOCTYPE html> <html> <head> <title>演示Vue</title> <style> </style> </head> <body> <div id='demo'> <ol> <li>111111</li> </ol> </div> </body> <script type="text/javascript"> var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; var list = document.querySelector('ol'); var Observer = new MutationObserver(function(mutations, instance){ mutations.forEach(function(mutation){ console.log(mutation); }); }); Observer.observe(list, { attributes: true }); // 设置节点的属性 会触发回调函数 list.setAttribute('data-value', '111'); // 重新设置属性 会触发回调 list.setAttribute('data-value', '2222'); // 删除属性 也会触发回调 list.removeAttribute('data-value'); </script> </html>
如下图所示:
除了基本的变动类型之外,options对象还可以设定以下属性
attributeOldValue: {boolean} 表示观察attributes变动时,是否需要记录变动前的属性值。
characterDataOldValue: {boolean} 表示观察characterData变动时,是否需要记录变动前的值。
attributeFilter {Array} 表示需要观察的特定属性 比如 ['class', 'src']
1. attributeOldValue 属性变动前,是否需要记录变动之前的值; 代码如下:
<!DOCTYPE html> <html> <head> <title>演示Vue</title> <style> </style> </head> <body> <div id='demo'> <ol> <li>111111</li> </ol> </div> </body> <script type="text/javascript"> var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; var list = document.querySelector('ol'); var Observer = new MutationObserver(function(mutations, instance){ mutations.forEach(function(mutation){ console.log(mutation); }); }); Observer.observe(list, { attributes: true, attributeOldValue: true }); // 设置节点的属性 会触发回调函数 list.setAttribute('data-value', '111'); // 删除属性 list.setAttribute('data-value', '2222'); </script> </html>
如上截图的oldValue 就是变动之前的值
2. characterData变动时,是否需要记录变动前的值。
如下代码:
<!DOCTYPE html> <html> <head> <title>演示Vue</title> <style> </style> </head> <body> <div id='demo'> <ol> <li>111111</li> </ol> </div> </body> <script type="text/javascript"> var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; var list = document.querySelector('ol'); var Observer = new MutationObserver(function(mutations, instance){ mutations.forEach(function(mutation){ console.log(mutation); }); }); Observer.observe(list, { childList:true, characterData:true, subtree:true, characterDataOldValue: true }); // 设置数据 触发回调 list.childNodes[0].data = "aaa"; // 重新设置数据 重新触发回调 list.childNodes[0].data = "bbbb"; </script> </html>
第一次设置数据,记录变动前的数据如下:
第二次设置数据,记录变动前的回调如下:
可以看到oldValue 的变动值;
attributeFilter {Array} 表示需要观察的特定属性 比如 ['class', 'src'];代码如下:
<!DOCTYPE html> <html> <head> <title>演示Vue</title> <style> </style> </head> <body> <div id='demo'> <ol> <li>111111</li> </ol> </div> </body> <script type="text/javascript"> var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; var list = document.querySelector('ol'); var Observer = new MutationObserver(function(mutations, instance){ mutations.forEach(function(mutation){ console.log(mutation); }); }); Observer.observe(list, { attributes: true, attributeFilter: ['data-value'] }); // 第一次设置属性 data-key 不会触发的,因为data-value 不存在 list.setAttribute('data-key', 1); // 第二次会触发 list.setAttribute('data-value', 1); </script> </html>
下面我们做一个简单的demo编辑器,首先给父级元素 ol 设置 contenteditable 让容器可编辑,然后构造一个observer 监听子元素的变化,每次回车的时候,控制台输出它的内容;如下代码:
<!DOCTYPE html> <html> <head> <title>演示Vue</title> <style> </style> </head> <body> <div id='demo'> <ol contenteditable oninput="" style='border: 1px solid red'> <li>111111</li> </ol> </div> </body> <script type="text/javascript"> var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; var list = document.querySelector('ol'); var Observer = new MutationObserver(function(mutations, instance){ mutations.forEach(function(mutation){ console.log(mutation); if(mutation.type === 'childList') { var list_values = [].slice.call(list.children).map(function(node) { return node.innerHTML; }).filter(function(s) { if(s === '<br>') { return false; } else { return true; } }); console.log(list_values); } }); }); Observer.observe(list, { childList: true }); </script> </html>
现在我们继续可以做一个类似于 input和textarea中的 valueChange的事件一样的,监听值变化,之前的值和之后的值,如下代码:
<!DOCTYPE html> <html> <head> <title>演示Vue</title> <style> </style> </head> <body> <div id='demo'> <ol contenteditable oninput="" style='border: 1px solid red'> <li>111111</li> </ol> </div> </body> <script type="text/javascript"> var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; var list = document.querySelector('ol'); var Observer = new MutationObserver(function(mutations, instance){ mutations.forEach(function(mutation){ var enter = { mutation: mutation, el: mutation.target, newValue: mutation.target.textContent, oldValue: mutation.oldValue }; console.log(enter); }) }); Observer.observe(list, { childList: true, attributes: true, characterData: true, subtree: true, characterDataOldValue: true, }); </script> </html>
注意: 对input 和 textarea 不起作用的。
编辑器统计字数的demo
<!DOCTYPE html> <html> <head> <title>演示Vue</title> <style> </style> </head> <body> <div id='editor' contenteditable style="width: 240px; height: 80px; border: 1px solid red;"></div> <p id="textInput">还可以输入100字</p> </body> <script type="text/javascript"> var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; var editor = document.querySelector('#editor'); var textInput = document.querySelector('#textInput'); var observer = new MutationObserver(function(mutations){ mutations.forEach(function(mutation) { if(mutation.type === 'characterData') { var newValue = mutation.target.textContent; textInput.innerHTML = "还可以输入" + (1000 - newValue.length+"字"); } }); }); observer.observe(editor, { childList: true, attributes: true, characterData: true, subtree: true, characterDataOldValue: true, }); </script> </html>