4

观察者模式介绍

观察者设计模式定义了对象间的一种一对多的依赖关系,以便一个对象的状态发生变化时,
所有依赖于它的对象都得到通知并自动刷新。

一些好的文章
观察者模式与委托模式的区别
深入理解JavaScript系列(32):设计模式之观察者模式
【Javascript设计模式3】-观察者模式

MutationObserver介绍

可以使用的配置

  • childList: *true =========================可以观察子元素
  • attributes: *true ========================可以观察属性
  • characterData: *true ====================可以观察数据
  • subtree: *true =========================可以观察所有后代
  • attributeOldValue: *true ==================可以保存属性旧值
  • characterDataOldValue: ==================*true 可以保存数据旧值
  • attributeFilter: ==========================设置指定观察属性元素集合

note: Mutation Observers 被限制可以观察Dom序列化状态 在观察textarea内内容改变时 无法触发

observe 一个被观察者对象 一份配置

void observe(
  Node target,
  MutationObserverInit options
);

disconnet 断开观察者与目标链接

void disconnect();

takeRecords 返回对象所有观察记录

Array takeRecords();

MutationObserver使用

不同浏览器之间存在兼容性问题

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

html

  <ol contenteditable oninput="">
    <li>Press enter and look at the console</li>
  </ol>

获取list dom

var list = document.querySelector('ol');

实例化MutationObserver

var Observer = new MutationObserver(function(mutations){
});

Mutation Observer 对象

图片描述

监测childList

Observer.observe(list, {
  childList: true
});

不过值得注意的是 这里观测的是list直接child 如果要观测child的child 就需要开启subtree
比如要检测这样的行为

list.childNodes[0].appendChild(document.createElement('div'));

observer需要更改配置

Observer.observe(list, {
  childList: true,
  subtree: true
});

(mutationObserver观测childlist) jsbin地址

observe 常见配置 及其解释

observer.observe(target, {childList:true,subtree:true})                 
//subtree属性让观察行为进行"递归",这时,以target节点为根节点的整棵DOM树发生的变化都可能会被观察到 
observer.observe(document, {childList:true,subtree:true})               
//如果target为document或者document.documentElement,则当前文档中所有的节点添加与删除操作都会被观察到
observer.observe(document, {childList:true,attributes:true,characterData:true,subtree:true})   
//当前文档中几乎所有类型的节点变化都会被观察到(包括属性节点的变化和文本节点的变化等)

observe 观察characterData

Observer.observe(list, {childList:true});  
//假设此时target的outHTML内容为<div>foo<div>,则: 
list.childNodes[0].data = "bar";           
//不会触发回调函数,因为childList只观察节点的新建与删除,
而这里target节点的子节点仍然只有一个,没有多,没有少
Observer.observe(list, {
childList:true,characterData:true});        
//加上characterData属性,允许观察文本节点的变化,行不行? 
list.childNodes[0].data = "sds";            
//还是不会触发回调函数,因为发生变化的是target节点的子节点,我们目前的目标节点只有一个,就是target.
Observer.observe(list, {
childList:true,characterData:true,subtree:true});  
//加上subtree属性,观察所有后代节点 
list.childNodes[0].data = "cha";    

(mutationObserver观测characterdata) jsbin地址

observe 观察attribute

Observer.observe(list, {attributes:true});                    
//只观察目标节点的属性节点
list.setAttribute("foo","bar");        
//不管foo属性存在不存在,都会触发回调函数
list.setAttribute("foo","bar");        
//即使前后两次的属性值一样,还是会触发回调函数
list.removeAttribute("foo");            
//移除foo属性节点,触发回调函数
list.removeAttribute("foo");            
//不会触发回调函数,因为已经没有属性节点可移除了
Observer.observe(list, {attributes:true,attributeFilter:["bar"]});     
//指定要观察的属性名
list.setAttribute("foo","bar");        
//不会触发回调函数,因为attributeFilter数组不包含"foo"
list.setAttribute("bar","foo"); 

(mutationObserver观测attribute) jsbin地址

我们开始做一个编辑器

功能 向前 向后并且按钮要能随时改变

ol设置为contenteditable时,当每次敲击回车时,ol的children会增加一个li 并且我们构造一个observer监听它子元素的变化, 这样我们便可以获得每次改变后所有新的children

  var observer = new MutationObserver(function(mutations) {  
    mutations.forEach(function(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, {
    attributes: true, 
    childList: true, 
    characterData: true
  });

(观察ol子元素的变化) jsbin地址

编辑器对与每行变化也需要监测, 我们可以自己构造一个entry保存每次数据

var observer = new MutationObserver(function (mutations) {
    // Whether you iterate over mutations..
    mutations.forEach(function (mutation) {
      // or use all mutation records is entirely up to you
      var entry = {
        mutation: mutation,
        el: mutation.target,
        value: mutation.target.textContent,
        oldValue: mutation.oldValue
      };
      console.log('Recording mutation:', entry);
    });
  });

(ol监测每一行变化) jsbin地址

编辑器统计字数

var observer = new  MutationObserver(function(mutations){ mutations.forEach(function(mutation){
    if(mutation.type == "characterData") {
    var newValue = mutation.target.textContent;
      textinputcount.innerHTML = "还可以输入"+(1000 - newValue.length+"字");
    }
  });

编辑器统计字数


andypinet
1.2k 声望41 粉丝

求一个常州 无锡 镇江的前端工作