加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

dojo事件驱动编程之事件绑定

发布时间:2020-12-16 21:58:10 所属栏目:百科 来源:网络整理
导读:转载博客:http://www.cnblogs.com/dojo-lzz/p/4687961.html dojo事件驱动编程之事件绑定 什么是事件驱动? 事件驱动编程是以事件为第一驱动的编程模型,模块被动等待 通知 (notification),行为取决于外来的突发事件,是事件驱动的,符合 事件驱动式编程

转载博客:http://www.cnblogs.com/dojo-lzz/p/4687961.html

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事件体系能够帮我们解决哪些问题?

  1. 解决浏览器兼容性问题:触发顺序、this关键字、规范化的事件对象(属性、方法)
  2. 可以在一个事件类型上添加多个事件处理函数,可以一次添加多个事件类型的事件处理函数
  3. 统一了事件的封装、绑定、执行、销毁机制
  4. 支持自定义事件
  5. 扩展组合事件

  dojo中处理浏览器事件的代码位于dojo/on模块中,在官网中可以查看该函数的签名:

  

  其中type可以是一个事件名称如:“click”

require(["dojo/on","dojo/_base/window"],function(on,win){
  var signal = on(win.doc,"click",255); line-height:1.5!important">function(){
    // remove listener after first event
    signal.remove();
     do something else...
  });
});

  亦可以是由逗号分隔的多个事件名组成的字符串,如:"dblclick,click"

require("dojo/on",255); line-height:1.5!important">function(on){
  on(element,"dblclick,touchend",255); line-height:1.5!important">function(e){
     handle either event
  });
});

  亦可以是由冒号分隔"selector:eventType"格式进行事件委托使用的字符串,如:".myClass:click"

".myClass:click",clickHandler); });

  亦可以是一个函数,如:touch.press、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,255); line-height:1.5!important">this);
    };
  如果target自己拥有on方法则调用target自己的on方法,如_WidgetBase类有自己的on方法,再比如jquery对象也会有自己的on方法,此处this关键字指向window。
  
  下面来看一下事件解析的过程:
  1. 如果type是方法,则交给type自身去处理;比如touch.press 、on.selector
  2. 多事件的处理;事件可能是通过逗号键分隔的字符串,所以将其变成字符串数组
  3. 对于事件数组依次调用on.parse
  4. 添加事件监听器
on.parse = 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);
    };

  

  接着看一下事件监听器的处理过程:
  1. 处理事件委托,dojo中事件委托的书写格式为:“selector:eventType”,直接交给on.selector处理
  2. 对与touchevent事件的处理,具体分析以后再说
  3. 对于stopImmediatePropagation的修正
  4. 支持addEventListener的浏览器,使用浏览器自带的接口进行处理
  5. 对于不支持addEventListener的浏览器进行进入fixAttach函数
View Code

  对于上面的分析我们可以得出几个结论:

  • 对于没有特殊EventType和普通事件都用addEventListener来添加事件了。
  • 而特殊EventType,则用了另一种方式来添加事件(fixAttach)。
  • 对于事件委托交给了on.selector处理

  

  来详细的看一下fixAttach:
  1、修正事件监听器,该过程返回一个闭包,闭包中对event对象进行修正,主要有一下几方面:
  • target
  • currentTarget
  • relatedTarget
  • stopPropagation
  • preventDefault
  • event的坐标位置兼容放到了dom-geometry的normalizeEvent中处理
  • keycode与charcode的处理
调用on中传入的事件监听器,如果监听器中掉用过stopImmediatePropagation缓存lastEvent,供以后使用
  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中返回一个匿名函数,匿名函数中做了几件事:
  1. 处理matchesTarget在matches方法中使用
  2. 如果eventType含有bubble方法进行特殊处理
  3. 其他普通情况,为代理元素绑定事件回调

  

  红框部分就是判断event.target是否匹配选择符,如果匹配则触发事件回调clickHandler.

  

  on.matches中做了以下几件事:
  1. 获取有效的matchesTarget,matchesTarget是一个拥有matches方法的对象,默认取dojo.query
  2. 对textNode做处理
  3. 检查event.target的祖先元素是否满足匹配条件
View Code

  对比dojo与jquery的事件处理过程,可以发现jQuery在事件存储上更上一筹:

  dojo直接绑定到dom元素上,jQuery并没有将事件处理函数直接绑定到DOM元素上,而是通过.data存储在缓存.cahce上。

  声明绑定的时候:

  • 首先为DOM元素分配一个唯一ID,绑定的事件存储在
    .cahce[唯一ID][.expand ][ 'events' ]上,而events是个键-值映射对象,键就是事件类型,对应的值就是由事件处理函数组成的数组,最后在DOM元素上绑定(addEventListener/attachEvent)一个事件处理函数eventHandle,这个过程由 jQuery.event.add 实现。

  执行绑定的时候:

  • 当事件触发时eventHandle被执行,eventHandle再去$.cache中寻找曾经绑定的事件处理函数并执行,这个过程由 jQuery.event. trigger 和 jQuery.event.handle实现。
  • 事件的销毁则由jQuery.event.remove 实现,remove对缓存$.cahce中存储的事件数组进行销毁,当缓存中的事件全部销毁时,调用removeEventListener/ detachEvent销毁绑定在DOM元素上的事件处理函数eventHandle。

  

  以上就是dojo事件模块的主要内容,如果结合Javascript事件机制兼容性解决方案来看的话,更有助于理解dojo/on模块。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读