DOM世界的观察者

标签: dom 世界 观察者 | 发表时间:2012-05-28 10:03 | 作者:司徒正美
出处:http://www.cnblogs.com/rubylouvre/

浏览器自带的观察者实在太多了。经典的不用说,就是onclick, attachEvent, addEventListner,可惜它们只是监听用户的行为。不过这当中有个特例是propertychange,当元素的属性,不管是自定义还是原生,只要发生改变,就会触发回调。我们还可以通过它的事件对象的propertyName知道那个元素发生改变。标准浏览器有个弱化版oninput,只能检测value值!

FF则有个__noSuchMethod__,只在用户调用方法时纠错用。后来,又搞出个逆天的Object.prototype.watch,由于元素节点在FF中也是Object的实例,其威力可想而已。但也有美中不足,我们不知道究竟是哪一个属性发生变化。

但这一状况随着浏览器对setter,getter的强力介入得到改善。早在Firefox 2.0+, Safari 3.0+, Chrome 1.0+ 与 Opera 9.5+,他们就口径一致地添加以下方法,支持这种新语法:


var lost = {
loc : "Island",
get location () {
//这里可以添加回调
return this.loc;
},
set location(val) {
//这里也可以搞小动作
this.loc = val;
}
};
lost.location = "Another island";

但这种新语法在IE8以下是会报致命错误,连try catch也挡不住,因此对这种兼容性极差的东西,程序员们不埋单,于是浏览器商又推销另一种新产品:



Object.defineProperty(document.body, "description", {
get : function () {
return this.desc;
},
set : function (val) {
this.desc = val;
}
});
document.body.description = "Content container";

著名的例子是在FF模拟outerHTML,不过这东西最终在FF11上实现了。


//http://stackoverflow.com/questions/1700870/how-do-i-do-outerhtml-in-firefox
if (typeof (HTMLElement) != "undefined" && !window.opera)
{
HTMLElement.prototype._____defineGetter_____("outerHTML", function()
{
var a = this.attributes, str = "<" + this.tagName, i = 0; for (; i < a.length; i++)
if (a[i].specified)
str += " " + a[i].name + '="' + a[i].value + '"';
if (!this.canHaveChildren)
return str + " />";
return str + ">" + this.innerHTML + "";
});
HTMLElement.prototype._____defineSetter_____("outerHTML", function(s)
{
var r = this.ownerDocument.createRange();
r.setStartBefore(this);
var df = r.createContextualFragment(s);
this.parentNode.replaceChild(df, this);
return s;
});
HTMLElement.prototype._____defineGetter_____("canHaveChildren", function()
{
return !/^(area|base|basefont|col|frame|hr|img|br|input|isindex|link|meta|param)$/.test(this.tagName.toLowerCase());
});
}

IE自有自己一套算盘,它使用Object.DefineProperty数据描述符实现 setter与getter。不过这东西在IE8有BUG,只能用于元素节点


//bug的详解见这里http://www.cnblogs.com/_franky/archive/2011/04/27/2030766.html
Object.defineProperty(document.body, "description", {
get : function () {
return this.desc;
},
set : function (val) {
this.desc = val;
}
});
document.body.description = "Content container";

// document.body.description will now return "Content container"

但setter,getter就是setter,getter,我们不应该在这里掺和,于是W3C提供了一系列高级的变动事件:

  • DOMAttrModified
  • DOMAttributeNameChanged
  • DOMCharacterDataModified
  • DOMElementNameChanged
  • DOMNodeInserted
  • DOMNodeInsertedIntoDocument
  • DOMNodeRemoved
  • DOMNodeRemovedFromDocument
  • DOMSubtreeModified

这下好了,无论是你是元素做增删改操作,还是元素的孩子们做增删改操作,还是对它的innerHTML或是属性进行增删改操作,它都提供监听。早期jQuery的Sizzle就是利用过DOMAttrModified清查调缓存的。我们可以在 这里查到它们的用法。但一个问题是, 浏览器商对此不怎么热衷,太复杂了,有太多了,太麻烦了,而且这类事件也不好用JS检测是否支持。

在ecma262v6中, FF开始推销它的一个好东西, Proxy!它相当于ecma262v5的数据描述符的强化版,但暂时没有其他浏览器商埋单。搞不好像IE8实现setter,getter那样,换个名字上场。

不过像propertychange这样的东西太重要了,老麻烦setInterval太不意思了。现在onhashchange, oninput都出来了,总有人干这事。时代在招唤!MutationObserver终于应运而生!而且MutationObserver是出乎意料的强大,把上面一系列Mutation Event的活都干了,而且出身好了,已列入W3C草案,MDC的文档,FF14说好会支持它,而chrome18已实现了。

DOM MutationObserver – reacting to DOM changes without killing browser performance.给出一个例子实现即时编辑:


<!doctype html>
<html>
<head>
<title>mass Framework</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
</head>
<body>

<ol contenteditable oninput="">
<li>Press enter</li>
</ol>
<script>
window.onload = function(){
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
var list = document.querySelector('input');

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,

});

}

</script>
</body>
</html>

如果翻看W3C, 我们可以找到更多用法:



<!doctype html>
<html>
<head>
<title>mass Framework</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>


</head>
<body>

<input value="aaa">
<script>
window.onload = function(){
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
var list = document.querySelector('input');
var observer = new MutationObserver(function(mutations) {
console.log(mutations)
mutations.forEach(function(record) {
if(record.attributeName == "value"){
console.log(record.target)
console.log(record.oldValue)
}
});
});
observer.observe(list, {
attributes: true,
childList: true,
characterData: true,
attributeOldValue :true,
attributeFilter:["value"]//只监听value属性,提高性能
});
list.setAttribute("value","bbb")
list.setAttribute("value","ccc")
}

</script>
</body>
</html>

如此一来我们就可以轻松实现propertychange的功能,也不用趟setter, getter的浑水了。现在它对前端实现MVC非常重要,负责对视图的变化进行监听,再配合已存的事件系统,形成一个密不透风的网,监听与揣测着用户的一举一动,堪比国安局啊,就像每家兰州拉面旁边必有个沙县小吃!

本文链接

相关 [dom 世界 观察者] 推荐:

DOM世界的观察者

- - 博客园_Ruby's Louvre
浏览器自带的观察者实在太多了. 经典的不用说,就是onclick, attachEvent, addEventListner,可惜它们只是监听用户的行为. 不过这当中有个特例是propertychange,当元素的属性,不管是自定义还是原生,只要发生改变,就会触发回调. 我们还可以通过它的事件对象的propertyName知道那个元素发生改变.

DOM详解

- - CSDN博客推荐文章
 1.XML解析方式分为两种:dom和sax.   (1)dom:(Document Object Model, 即文档对象模型) 是 W3C 组织推荐的处理 XML 的一种方式.   (2) sax:(Simple API for XML) 不是官方标准,但它是 XML 社区事实上的标准,几乎所有的 XML 解析器都支持它.

javaScript DOM使用

- - CSDN博客互联网推荐文章
通过 HTML DOM,可访问 JavaScript HTML 文档的所有元素. 1 修改HTML元素内容. document.write(Date()); //在输入流中直接写 document.getElementById(id).innerHTML=new HTML. //改变已经有的元素内容 document.getElementById("image").src="landscape.jpg";.

DOM优化

- - JavaScript - Web前端 - ITeye博客
1,Javascript语音与DOM操作就像孤岛. 他们之间的通行是要借助船的,而使用船是有很大成本的. 所以,要先做完一个再做另一个,最好不要交替进行. 如:添加1000个li时,先用一个字符串拼接好,最后一次追加到父节点,而不是向父节点追加1000次,一次追加一个. 如:添加1000个li时,先放到fragment,再添加到UL中.

Android DOM解析XML

- - CSDN博客移动开发推荐文章
if(personChilds.item(y).getNodeType()==Node.ELEMENT_NODE){//判断当前节点是否是元素类型节点. 作者:jaycee110905 发表于2013-2-7 21:04:29 原文链接. 阅读:78 评论:0 查看评论.

jquery和DOM比较

- - JavaScript - Web前端 - ITeye博客
1、window.onload和$(document).ready()的区别. 必须等整个网页中所有的内容加载完毕后(包括图片)才能执行. 网页中所有DOM结构绘制完毕后就执行,可能DOM元素并没有加载完. 2、jquery对象和DOM对象的区别. Juery对象是包装DOM后的产生的对象,DOM是原生对象,是一个基本的文档结构.

Javascript的DOM操作

- - CSDN博客Web前端推荐文章
返回对拥有指定id的第一个对象进行访问. 返回带有指定名称的节点集合. 返回带有指定标签名的对象集合. 返回带有指定class名称的对象集合. 参数:是否复制原节点的所有属性. 注意:IE会忽略节点间生成的空白文本节点(例如,换行符号),而Mozilla不会这样做. 在删除指定节点的时候不会出错,但是如果要删除最后一个子结点或者是第一个子结点的时候,就会出现问题.

javascript之XML DOM的解析

- - ITeye博客
javascript之XML DOM的解析. . 长春. 吉林市. 四平. 松原. 通化. .

也许,DOM 不是答案

- - 阮一峰的网络日志
有一个词"手机网站"(mobile web),指供手机浏览的网站,但它是不存在的. 人们提到"移动互联网"的时候,其实专指另外一样东西:手机App. 一、Web App vs. 比起手机App,网站有一些明显的优点. 快速部署:升级只需在服务器更新代码. 超链接:可以与其他网站互连,可以被搜索引擎检索.

用 Virtual DOM 加速开发

- - 前端观察
简聊(by Teambition)产品前端中使用了 React,最初开发时使用的 Backbone 搭配 doT.js 模版渲染界面,实践下来效果提升了很多. 我们希望能吸引更多同学能够运用 Virtual DOM 改进前端开发,所以这篇文章会主要介绍 React 当中 Virtual DOM 相关的知识.