为了客姥爷的用户体验,我们必须做兼容处理,我们可以

1.通过提供统一的api(不兼容就是api不统一,有没有一种越走越远的感觉...),通过鸭辨式找到浏览器提供的相似功能进行兼容

2.使用浏览器提供的最基本的功能进行模拟

 

兼容模型

在事件模型兼容性中,我们需要兼容以上浏览器,其中ie6,7,8使用的是ie事件模型,其他的则为标准dom模型,可以简单的理解为非ie即dom,大致有2中思路

1.鸭辨式,有dom事件接口即使用,否则就是用ie事件接口

2.通过传统事件模拟事件高阶性质

鸭辨式

            Element.prototype.addEvent = function(type, fn, useCapture) {
                if (this.addEventListener) {
                    this.addEventListener(type, fn, !!useCapture); //dom模型
                } else {
                    this.attachEvent("on" + type, fn); //ie模型
                }
            }

标准的鸭辨式,有点代理的意思,javascript可以进行方法的传递/赋值,所以,也可以这样

        Element.prototype.addEvent = this.addEventListener?this.addEventListener:function(type, fn){
            this.attachEvent("on" + type, fn);
        }

利用了方法赋值的特性,使的检测只进行一次(当然,这里面有坑,不管他),或者干脆将addEvent取名为addEventListener

无论如何,事件兼容,我们做到了,但这里面有几个坑

1.鸭辨式总是不准的(尤其是国内,尼玛,提供个空接口是几个意思)

2.ie模型和dom模型也是一点点在规范,每个版本浏览器都有一点点的不一样(一群一点点比一样会要人命的)

所以,通过传统方式自制事件模型也是必须的

传统式/自制事件模型

先按照dom模型画个图图压压惊,在模拟事件中,只是用传统方式,即onxxx系列

这里我认为传播不需要模拟,毕竟目前的浏览器都会有传播的概念(我模拟过一次,很容易就执行2遍),这里可以说是只模拟多投事件,可以看到就是一个事件数组的封装

模拟多投事件,第一件事就是解决数组,我可以将他放在闭包中,也可以将它"序列化",将其与dom进行连接

1.使用dom与fns直接进行关联

非常简单,dom1.fns就可以实现的结构

            Element.prototype.addEvent = function(type, fn) {
                var fns= this.fns;
                if(!fns){
                    this.fns=[];
                    fns=this.fns;
                }
                var fs = this.fns[type];
                if (fs) {
                    fs.push(fn);
                } else {
                    fns[type] = [fn];
                }
                this["on" + type] = this.proxy;
            }
            Element.prototype.proxy = function(event) {
                var fns=this.fns,type = event.type;
                if(!fns||!fns[type]){
                    return;
                }
                var fs = fns[type];
                if (fs) {
                    for (var index in fs) {
                        fs[index]();
                    }
                }
            }

多投事件就已经被模拟出来了

2.使用闭包

闭包是函数中的函数,是函数调用过程中,对作用域链的"回调",

            var fns={};
            Element.prototype.addEvent =function(type, fn){
                
                var fs=fns[type];
                if(fs){
                    fs.push(fn);
                }else{
                    fns[type]=[fn];
                }
                this["on"+type]=this.proxy;
            }
            
            Element.prototype.proxy=function(event){
                var type=event.type;
                var fs=fns[type];
                if(fs){
                    for(var  index in fs ){
                    fs[index]();    
                    }
                }
            }

将上面这段话丢到自执行中,就不会直接找到fns的句柄了,以此产生闭包,当然因为通过原形进行赋值,可以发现,此时的fns是公共的也就是将原本的fns改成下面的方式

 

 

(function(){
            var allfns={};
            Element.prototype.addEvent =function(type, fn){
                var fns=allfns[this];
                if(!fns){
                    allfns[this]={};
                    fns=allfns[this];
                }
                var fs=fns[type];
                if(fs){
                    fs.push(fn);
                }else{
                    fns[type]=[fn];
                }
                this["on"+type]=this.proxy;
            }
            
            Element.prototype.proxy=function(event){
                var type=event.type;
                var fns=allfns[this];
                var fs=fns[type];
                if(fs){
                    for(var  index in fs ){
                    fs[index]();    
                    }
                }
            }})()

这样就无敌了,对吧,我也是这样认为的,但尝试一下就会发现还是有可能重复,尤其是相似的Element之间(比如2个input),原因就在于json中的key(他必须是字符串,并非引用), allfns[this] 会将this的toString/valueOf(详细可看转换规则),转换为key值,所以
allfns[this]有时就等价于allfns["[object HTMLInputElement]"]

json的key必须是字符串,记得当年有道面试题就是用js写一个map,原因就是这个,我们可以修改toString,将他改成hash,或者直接提供一个hash接口,当然通过自增的方式(id),也是不错的选择

 

            (function(){
            var allfns={};
            var sequence=1;
            Element.prototype.addEvent =function(type, fn){
                var id=this.sequence;
                if(!id){
                    id=this.sequence=sequence++;
                }
                var fns=allfns[id];
                if(!fns){
                    fns=allfns[id]={};
                }
                var fs=fns[type];
                if(fs){
                    fs.push(fn);
                }else{
                    fns[type]=[fn];
                }
                this["on"+type]=this.proxy;
            }
            
            Element.prototype.proxy=function(event){
                var id=this.sequence;
                if(!id){
                    return;
                }
                var type=event.type;
                var fns=allfns[id];
                var fs=fns[type];
                if(fs){
                    for(var  index in fs ){
                    fs[index]();    
                    }
                }
            }})()

一个可以添加的模型产生了,现在再考虑一下事件的删除,我们可以通过delete删除,但数组并不是链式的,删了以后坑依然留着,如果这不算bug的话,一个简单的兼容事件模型已经产生了

我们还可以参考jquery的事件模型(反正当我写完一个事件模型以后,在看jquery,我都想告jquery侵权- -!)

 

posted on 2015-04-05 23:16  Glimis  阅读(163)  评论(0编辑  收藏  举报