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

  • 相关阅读:
    Go 单元测试、基准测试、并发基准测试
    Go url编码和字符转码
    ssh 登录进入 docker container
    Python 开发
    Ethereum 源码分析之 accounts
    Ethereum 源码分析之框架
    数据库视图
    共识算法:PBFT、RAFT
    JQuery Mobile
    Android Studio
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/2520721.html
Copyright © 2011-2022 走看看