9、ExtJs——Ext基础架构--事件机制

来源:互联网 发布:日本文学 知乎 编辑:程序博客网 时间:2024/05/29 11:43

对于事件

- 追朔到原始的事件模型,也就是0级DOM事件模型。非常通俗的说,就是给HTML元素里加一个事件,这种方法有一个硬伤,就是不能同时对元素注册多个处理函数。

- W3C的专家定义2级DOM事件模型也就产生了,也就是所谓的标准事件模型。API如下定义:
    - element.addEventListener(type,listener,useCapture);
    - element.removeEventListener(type,listener,useCapture);

- 这又牵扯到了事件流,也就是事件的传播。W3C的专家把事件分成了3个阶段:(1)捕获;(2)目标;(3)冒泡;

- 事件对象(event)这个对象是W3C的标准。但是IE不支持,只能用window.event去获取,这里又引出了一个最头疼的问题,就是浏览器差异。

//1、在早些时候,是不存在事件这个概念的,开发者用javascript的2个函数去模拟事件的机制(window.setTimeOut/window.setInterval)//2、由于很多原因,比如效率太低,人们开始发明了最原始的0级DOM事件模型,为元素添加一个事件,在事件上绑定一个处理函数//注意:这种模型有一个致命的硬伤:就是不能为元素的事件添加多个处理函数/** * 0级DOM事件模型window.onload = function(){var inp = document.createElement('input');inp.id = 'inp1';inp.value = '点击';inp.type = 'button';inp.onclick = function(){alert('执行了');};inp.onclick = function(){    //覆盖上一个alert(this == window);    //这个为falsealert(this == inp);       //这个为true  alert('我也执行了');};document.body.appendChild(inp);};*//** * 如果在html或jsp页面上,如下 * <script type="javascript/text"> * function test1(){ * alert(this == window)  //这个为true * alert('1111'); * } * function test2(){ * alert('22222'); * } * </script> * <body> * <input type="button" value="我也点击" onclick="test1();test2();" * </body> * 这两个函数都执行,这种形式的作用域是window */// //function test1(){alert(this == window);};//function test2(){alert(222);};//3、在0级DOM事件模型之后W3C成立,觉得0级事件模型不好,推出了2级DOM事件模型(标准DOM事件模型)// element.addEventListener(type,listener,useCapture);// element.removeEventListener(type,listener,useCapture);//window.onload = function(){/**var inp = document.createElement('input');inp.id = 'inp1';inp.value = '点击';inp.type = 'button';//type :事件类型  listener:这个事件的绑定函数   useCapture(boolean):(事件传播:true=捕获/false=冒泡)inp.addEventListener('click',test1,false);inp.removeEventListener('click',test1,false);inp.addEventListener('click',test2,false);document.body.appendChild(inp);*///IE浏览器(6\7\8版本):使用attachEvent();detachEvent();9、10已经支持W3C标准了/**var inp = document.createElement('input');inp.id = 'inp1';inp.value = '点击';inp.type = 'button';inp.attachEvent('onclick',test1);inp.detachEvent('onclick',test1);inp.attachEvent('onclick',test2);document.body.appendChild(inp);*///}; //对于事件的传播机制:W3C:1、捕获  2、目标()命中   3、冒泡//W3C提供了一个关键字event 事件对象  /ie678:window.eventwindow.onload = function(){varinp = document.createElement('input');inp.id = 'inp1';inp.value = '点击';inp.type = 'button';inp.addEventListener('click',function(event){alert('input执行了');event.stopPropagation();  //阻止冒泡的发生},false);var div = document.createElement('div');div.addEventListener('click',function(){alert('div执行了')},false);document.body.addEventListener('click',function(){alert('body执行了')},false);div.appendChild(inp);document.body.appendChild(div);};
对于事件流,也就是事件的传播,W3C的专家把事件分成了3个阶段:(1)捕获;(2)目标;(3)冒泡;

高级事件

- 基本事件是什么?就类似于click、keypress、focus、mouseover等这些事件都是浏览器定义好的内置事件,我们直接使用即可。对于高级事件,无非就是自己去设计一个事件,比如实际项目中,通常都伴随业务逻辑,可能是增删改查等,这些事件都是非原生事件,也就是浏览器无法自行判断触发的,但是我们确实有需求去实现它们。

- 对于如何实现自定义事件,还需要了解标准事件的使用原理,然后做一个简单地分析,考虑3点:
    - 如何注册事件?
    - 如何触发事件?
    - 如何删除事件?

- 其实浏览器的事件内部使用的是javascript经典的观察者模式去实现的,那么我们也可以模拟一个观察者模式,为自己设计事件!

模拟实现:使用观察者模式模拟事件

设计分析:

- 首先需要一个事件的定义者,类似浏览器一样能自动分辨出所触发的任意内置事件,我们叫他Observable,它是我们要定义的类;然后应该有一个触发事件的对象(就类似浏览器里的元素),也就是事件源,这个类可以是Observable的子类。

- 第二,Observable这个对象的实例可能会有多个可以触发的事件,我们随意定义2个自己的事件,开始‘start’、停止‘stop’,这2个自定义的事件名称也就是我们要进行的事件。这2个事件名称必须要属于Observable这个类

- 第三,因为仅仅定义名称是不行的,需要为自己定义的事件名称绑定相关的函数,当然函数可以是多个(一个事件可以绑定多个函数)然后去执行它们。你需要有一个数据结构负责维护事件名称与所绑定函数直接的关系

- 第四,新增事件类型,也就是去添加你自己的事件名称

- 第五,添加监听函数,也就是做一个事件名称与函数的绑定方法

- 第六,相应的也应该有一个移除事件的方法

- 第七,触发事件,就是调用这个事件名称所对应的所有函数即可。

- 最后,给函数起一个别名,从而方便开发者使用。

//利用观察者模式去实现自定义的事件//1、首先我们需要一个事件的定义者,类似浏览器一样能自动分辨//1、由于浏览器自己能定义内置的事件(click/blur...)//我们也应该有一个类似于浏览器这样的类,这个类自己去内部定义一些事件(自定义事件)var Observable = function(){//承装自己定义的事件类型的this.events = ['start','stop'];//我们应该设计一种数据类型,这种数据类型就可以去维护自定义事件类型和相关绑定函数的关系,结构如下//'start':[fn1,fn2...],//'stop':[]this.listeners = {};};//2、添加新的自定义事件类型Observable.prototype.addEvents = function(eventname){this.events.push(eventname);};//3、为自己的事件类型绑定相应的函数(添加事件监听)Observable.prototype.addListener = function(eventname,fn){//做一个容错处理if(this.events.indexOf(eventname) == -1){this.addEvents(eventname);}//到这里,事件类型肯定存在,那么var arr = this.listeners[eventname];//如果当前这个函数数组不存在,那么我们要为这个事件类型绑定新添加的函数if(!arr){arr = [fn];} else {             //如果存在,当前这个事件类型所对应的函数数组不为空,已绑定过函数if(arr.indexOf(fn) == -1){arr.push(fn);}}//重新维护一下事件类型和所绑定的函数数组的关联关系this.listeners[eventname] = arr;};//4、移除事件监听Observable.prototype.removeListener = function(eventname,fn){//如果要移除的事件类型,在对象里没有定义if(this.events.indexOf(eventname) == -1){return ;}//到这一步,就是你要移除的事件类型是我当前对象里存在的var arr = this.listeners[eventname];if(!arr){return ;}//到这一步,证明arr里面是有绑定函数的//判断 如果当前fn函数在我的函数数组中存在,就移除if(arr.indexOf(fn) != -1){arr.splice(arr.indexOf(fn),1);}};//5、如何让事件触发:就是调用这个事件类型所对应的所有函数执行即可Observable.prototype.fireEvent = function(eventname){//如果当前传递的事件为空或者事件类型不存在,直接返回if(!eventname || (this.events.indexOf(eventname) == -1)){return ;}//到这一步,一定存在这个事件var arr = this.listeners[eventname];if(!arr){return ;}for(var i = 0,len = arr.length;i<len;i++){var fn = arr[i];fn.call(fn,this);}};//javascript的习惯,给原型对象的方法起一个别名,方便使用Observable.prototype.on = Observable.prototype.addListener;Observable.prototype.un = Observable.prototype.removeListener;Observable.prototype.fr = Observable.prototype.fireEvent;//Observable相当于一个浏览器var ob = new Observable();  // 被观察者  / 观察者//子类继承Observable          //观察者var fn1 = function(){alert('fn1.......');};ob.on('start',fn1);var fn2 = function(){alert('fn2.......');};//ob.on('stop',fn2);ob.on('start',fn2);ob.un('start',fn1);ob.fr('start');//ob.fr('stop');ob.on('run',function(){alert('run...');});ob.fr('run');
//Ext.util.Observable 类是为了为开发者提供一个自定义事件的接口
//观察者模式:(类比:报社、订阅者)被观察者、观察者
//Ext.util.Observable -->被观察者(具有触发)
//所有继承(混入)Ext.util.Observable类的对象(子类) --->观察者(具有订阅和退订)
事件工具类:

Ext.EventManager,对于事件,我们需要屏蔽浏览器的差异,需要一个事件管理器,用于屏蔽一切浏览器差异,这个类,就是为了屏蔽浏览器差异,暴漏统一的调用接口。是一个静态工具类

//Ext.EventManager:封装浏览器自带的事件,并且解决了浏览器的差异问题var MyExt = {};MyExt.EventManager = {//添加监听addListener:function(el,ename,fn,useCapture){if(el.addEventListener){el.addEventListener(ename,fn,useCapture)} else if(el.attachEvent){el.attachEvent('on' + ename,fn);}},//移除监听removeListener:function(el,ename,fn,useCapture){if(el.removeEventListener){el.removeEventListener(ename,fn,useCapture)} else if(el.detachEvent){el.detachEvent('on' + ename,fn);}}//w3c  ====event    /ie window.event};MyExt.EventManager.on = MyExt.EventManager.addListener;MyExt.EventManager.un = MyExt.EventManager.removeListener;window.onload = function(){var btn = document.getElementById('btn');MyExt.EventManager.on(btn,'click',function(){alert('执行了');},false);MyExt.EventManager.on(btn,'click',function(){alert('又执行了');},false);};
对于事件系统,设计了两套机制,一套机制做了对自定义事件的处理,另一套机制做了对原生浏览器事件的差异处理。如果现在有一个需求,就是点击按钮,然后触发一个自定义的事件,那该如何去做?

很简单,就是把这两套系统综合起来,无非就是点击按钮的时候里面调用一下自己所定义的事件“fire”方法即可。
对于Ext来说,它的事件机制的核心设计就是我们模拟的这两套机制,简单做一个说明:Ext的事件分为浏览器事件和自定义事件。通过Observable接口提供了一套完全自定义的事件机制,然后再通过EventManager事件工具类对原生事件的一次封装,屏蔽了浏览器之间的差异。最后保证Observable、EventManager它们两套机制暴漏出完全相同的接口,这两套机制相互配合,相互并行。可以随意的通过on、un方法绑定原生事件或自定义事件来完成事件的处理。从而实现了非常强大的功能。

熟练使用Ext事件:

    - 为底层元素注册事件:on 、un方法使用:
    - Ext.EventManager.on(el,eventName,fn,[scope,options...])
    - 常用options:preventDefault、stopPropagation、delay、single
    - Ext.EventManager.un(同上)
    - 三种绑定事件的方式:
    - Ext.EventManager.on(el,ename,fn);
    - Ext.EventManager.on(el,{ename1:fn1,ename2:fn2});
    - Ext.EventManager.on(el,{ename1:{fn:fun},ename2:{fn:fun}});

为Ext的UI组件绑定事件:两种方式

在listeners里注册事件(组件里的配置项),单独为组件批量注册事件

Ext.onReady(function(){//为Ext的UI组件绑定事件//1:直接在组件内部添加listeners配置项即可/**var win = Ext.create('Ext.window.Window',{title:'UI组件之事件实例1',width:400,height:300,renderTo:Ext.getBody(),tbar:[{text:'dianjiwo',id:'aa'}],listeners:{           //在这个配置项对象中加入事件即可show:function(){alert('我展示出来了');},close:function(){alert('关闭事件');},render:function(){alert('组件渲染的时候执行事件');},click:{element:'el',fn:function(){alert('点击组件内部的body');}}}});win.show();  //即使没有这一句render也会执行*///2:使用组件的引用为组件绑定一些事件var win = Ext.create('Ext.window.Window',{title:'UI组件之事件实例1',width:400,height:300,renderTo:Ext.getBody()});//Ext.Window混入了Ext.Observable类,可以使用其on方法//win.on('show',function(){//alert('我展示出来了');//);win.on({'show':function(){alert('我展示出来了');},'close':function(){alert('关闭事件');},'render':function(){               //这种方法绑定render事件不会执行,太迟了alert('组件渲染的时候执行事件');}});win.show();});
要注意render事件(渲染)的执行时机。组件创建完在绑定的方法,render事件不会执行

自定义事件的注册

Ext.onReady(function(){//Ext.util.Observable 自定义事件类//1:最简单的自定义事件/**var win = Ext.create('Ext.window.Window',{title:'简单的自定义事件',width:400,height:300,renderTo:Ext.getBody(),listeners:{show:function(){//1.3触发自定义事件的时机win.fireEvent('myEvent');}}});//1.1添加事件类型win.addEvents('myEvent');//1.2添加事件的监听win.on('myEvent',function(){alert('my event...');});win.show();*///2、为自己定义的类去添加事件的支持/**Ext.define('Employee',{mixins:{observable:'Ext.util.Observable'},constructor:function(config){this.mixins.observable.constructor.call(this,config);this.addEvents('fired','quit');}});var newEmployee = new Employee({listeners:{quit:function(){alert('has quit!');}}});newEmployee.fireEvent('quit');});*///3、单次运行监听器的使用,single配置项在组件中的用途/**var win = Ext.create('Ext.window.Window',{title:'我是单次执行监听器的使用',width:400,height:300,renderTo:Ext.getBody(),listeners:{render:function(){alert('把组件渲染到body上,整个过程只执行一次');},single:true,        //当前这个事件监听执行一次之后就自动销毁了delay:3000         //延迟执行事件监听的执行}});win.show();*///4、对于事件的挂起和恢复示例/**var btn1 = Ext.create('Ext.button.Button',{text:'挂起',renderTo:Ext.getBody(),handler:function(){btn3.suspendEvents();}});var btn2 = Ext.create('Ext.button.Button',{text:'恢复',renderTo:Ext.getBody(),handler:function(){btn3.resumeEvents();}});var btn3 = Ext.create('Ext.button.Button',{text:'按钮',renderTo:Ext.getBody(),listeners:{'mouseover':function(){alert('执行了。。。');}}});*///5、事件的转发机制var win = Ext.create('Ext.window.Window',{title:'事件的转发',width:400,height:300,renderTo:Ext.getBody(),listeners:{myEvent:function(){alert('我是自定义事件,转发给btn');}}});var btn = Ext.create('Ext.Button',{text:'按钮',renderTo:Ext.getBody(),handler:function(){btn.fireEvent('myEvent');}});win.show();//事件的转发机制:1:转发给的对象  2:转发的事件类型数组win.relayEvents(btn,['myEvent']);});
最后的总结:

Ext的事件主要做了三个最重要的工作:

 - (1)屏蔽了浏览器的差异
 - (2)原生事件和自定义事件可以并行的自由使用
 - (3)强大的扩展性

可以说我们这个Ext,是运行在Observable这个类之上的。Ext中大部分类都需要混入Ext.util.Observable,从而提供对于事件的支持


0 0
原创粉丝点击