好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

DOM世界的观察者

DOM世界的观察者

浏览器自带的观察者实在太多了。经典的不用说,就是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 + "<!--" + this .tagName + "-->" ; 

                 }); 

                 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检测是否支持。

在ecma262v6k ,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非常重要,负责对视图的变化进行监听,再配合已存的事件系统,形成一个密不透风的网,监听与揣测着用户的一举一动,堪比国安局啊,就像每家兰州拉面旁边必有个沙县小吃!

 

 

标签:  javascript

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于DOM世界的观察者的详细内容...

  阅读:45次