观察者模式介绍
观察者设计模式定义了对象间的一种一对多的依赖关系,以便一个对象的状态发生变化时,
所有依赖于它的对象都得到通知并自动刷新。
一些好的文章
观察者模式与委托模式的区别
深入理解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
});
编辑器对与每行变化也需要监测, 我们可以自己构造一个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);
});
});
编辑器统计字数
var observer = new MutationObserver(function(mutations){ mutations.forEach(function(mutation){
if(mutation.type == "characterData") {
var newValue = mutation.target.textContent;
textinputcount.innerHTML = "还可以输入"+(1000 - newValue.length+"字");
}
});
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。