dojo事件驱动编程之事件绑定
什么是事件驱动?
事件驱动编程是以事件为第一驱动的编程模型,模块被动等待通知(notification),行为取决于外来的突发事件,是事件驱动的,符合事件驱动式编程(Event-Driven Programming,简称EDP)的模式。 何谓事件?通俗地说,它是已经发生的某种令人关注的事情。在软件中,它一般表现为一个程序的某些信息状态上的变化。基于事件驱动的系统一般提供两类的内建事件(built-in event):一类是底层事件(low-level event)或称原生事件(native event),在用户图形界面(GUI)系统中这类事件直接由鼠标、键盘等硬件设备触发;一类是语义事件(semantic event),一般代表用户的行为逻辑,是若干底层事件的组合。比如鼠标拖放(drag-and-drop)多表示移动被拖放的对象,由鼠标按下、鼠标移动和鼠标释放三个底层事件组成。 还有一类用户自定义事件(user-defined event)。它们可以是在原有的内建事件的基础上进行的包装,也可以是纯粹的虚拟事件(virtual event)。除此之外,编程者不但能定义事件,还能产生事件。虽然大部分事件是由外界激发的自然事件(natural event),但有时程序员需要主动激发一些事件,比如模拟用户鼠标点击或键盘输入等,这类事件被称为合成事件(synthetic event)。这些都进一步丰富完善了事件体系和事件机制,使得事件驱动式编程更具渗透性。
上图为一个典型的事件驱动式模型。事件处理器事先在关注的事件源上注册,后者不定期地发表事件对象,经过事件管理器的转化(translate)、合并(coalesce)、排队(enqueue)、分派(dispatch)等集中处理后,事件处理器接收到事件并对其进行相应处理。通过事件机制,事件源与事件处理器之间建立了松耦合的多对多关系:一个事件源可以有多个处理器,一个处理器可以监听多个事件源。再换个角度,把事件处理器视为服务方,事件源视为客户方,便是一个client-server模式。每个服务方与其客户方之间的会话(session)是异步的,即在处理完一个客户的请求后不必等待下一请求,随时可切换(switch)到对其他客户的服务。 在web环境中事件源由DOM充当,事件管理器对于web开发者来说是透明的,由浏览器内部管理,事件处理器便是我们绑定在dom事件中的回调函数。 Web事件处理流程 DOM2.0模型将事件处理流程分为三个阶段:一、事件捕获阶段,二、事件目标阶段,三、事件起泡阶段。如图:
事件捕获:当某个元素触发某个事件(如onclick),顶层对象document就会发出一个事件流,随着DOM树的节点向目标元素节点流去,直到到达事件真正发生的目标元素。在这个过程中,事件相应的监听函数是不会被触发的。 事件目标:当到达目标元素之后,执行目标元素该事件相应的处理函数。如果没有绑定监听函数,那就不执行。 事件起泡:从目标元素开始,往顶层元素传播。途中如果有节点绑定了相应的事件处理函数,这些函数都会被一次触发。如果想阻止事件起泡,可以使用e.stopPropagation()(Firefox)或者e.cancelBubble=true(IE)来组织事件的冒泡传播。 然而在此末法时代,浏览器两大派别对于事件方面的处理,常常让前端程序员大伤脑筋,所以任何前端库首先要对事件机制进行统一。 dojo中的事件绑定 dojo事件体系能够帮我们解决哪些问题?
dojo中处理浏览器事件的代码位于dojo/on模块中,在官网中可以查看该函数的签名:
其中type可以是一个事件名称如:“click” require(["dojo/on","dojo/_base/window"],function(on,win){ var signal = on(win.doc,"click",function(){ // remove listener after first event signal.remove(); // do something else... }); }); 亦可以是由逗号分隔的多个事件名组成的字符串,如:"dblclick,click" require("dojo/on",function(on){ on(element,"dblclick,touchend",function(e){ // handle either event }); }); 亦可以是由冒号分隔"selector:eventType"格式进行事件委托使用的字符串,如:".myClass:click" require(["dojo/on","dojo/_base/window","dojo/query"],win){
on(win.doc,".myClass:click",clickHandler);
});
亦可以是一个函数,如:touch.press、on.selector() require(["dojo/on","dojo/mouse","dojo/query!css2"],mouse){
on(node,on.selector(".myClass",mouse.enter),myClassHoverHandler);
});
查看一下on函数的源码 var on = function(target,type,listener,dontFix){ if(typeof target.on == "function" && typeof type != "function" && !target.nodeType){ // delegate to the target's on() method,so it can handle it's own listening if it wants (unless it // is DOM node and we may be dealing with jQuery or Prototype's incompatible addition to the // Element prototype return target.on(type,listener); } // delegate to main listener code return on.parse(target,addListener,dontFix,this); };
如果target自己拥有on方法则调用target自己的on方法,如_WidgetBase类有自己的on方法,再比如jquery对象也会有自己的on方法,此处this关键字指向window。
下面来看一下事件解析的过程:
on.parse = function(target,matchesTarget){ if(type.call){ // event handler function // on(node,touch.press,touchListener); return type.call(matchesTarget,target,listener); } if(type instanceof Array){ // allow an array of event names (or event handler functions) events = type; }else if(type.indexOf(",") > -1){ // we allow comma delimited event names,so you can register for multiple events at once var events = type.split(/s*,s*/); } if(events){ var handles = []; var i = 0; var eventName; while(eventName = events[i++]){ handles.push(on.parse(target,eventName,matchesTarget)); } handles.remove = function(){ for(var i = 0; i < handles.length; i++){ handles[i].remove(); } }; return handles; } return addListener(target,matchesTarget); };
接着看一下事件监听器的处理过程:
View Code
对于上面的分析我们可以得出几个结论:
来详细的看一下fixAttach:
1、修正事件监听器,该过程返回一个闭包,闭包中对event对象进行修正,主要有一下几方面:
2、对于低版本浏览器防止在frames和为链接到DOM树中元素添加事件时引起的内存泄露,这里自定义一个Event对象,将所有的事件监听器作为属性添加到这个Event对象上。
3、不在2条件中的情况使用aspect.after构造一个函数链来存放事件监听器,这就保证了监听器的调用顺序与添加顺序一致。
View Code
关于aspect.after的具体工作原理,请看我的这篇文章:Javascript事件机制兼容性解决方案
接下来我们看一下委托的处理:
为document绑定click事件,click事件出发后,判断event.target是否满足选择符“button.myclass”,若满足则执行clickHandler。为什么要判断event.target是否满足选择条件,document下可能有a、也可能有span,我们只需要将a的click委托给document,所以要判断是否满足选择条件。委托过程的处理主要有两个函数来解决:on.selector、on.matches.
on.selector中返回一个匿名函数,匿名函数中做了几件事:
红框部分就是判断event.target是否匹配选择符,如果匹配则触发事件回调clickHandler.
on.matches中做了以下几件事:
View Code
对比dojo与jquery的事件处理过程,可以发现jQuery在事件存储上更上一筹: dojo直接绑定到dom元素上,jQuery并没有将事件处理函数直接绑定到DOM元素上,而是通过.data存储在缓存.cahce上。 声明绑定的时候:
执行绑定的时候:
以上就是dojo事件模块的主要内容,如果结合Javascript事件机制兼容性解决方案来看的话,更有助于理解dojo/on模块。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |