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

  • 相关阅读:
    BZOJ 1391: [Ceoi2008]order
    BZOJ 4504: K个串
    2019 年百度之星·程序设计大赛
    POJ 2398 Toy Storage (二分 叉积)
    POJ 2318 TOYS (二分 叉积)
    HDU 6697 Closest Pair of Segments (计算几何 暴力)
    HDU 6695 Welcome Party (贪心)
    HDU 6693 Valentine's Day (概率)
    HDU 6590 Code (判断凸包相交)
    POJ 3805 Separate Points (判断凸包相交)
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/2520721.html
Copyright © 2011-2022 走看看