google closure 笔记-事件框架

来源:互联网 发布:samba的共享端口 编辑:程序博客网 时间:2024/05/11 02:45

Event事件框架

Event 类: 事件对象,在事件监听中的回调函数中传递的参数e就是这个类的实例。

如果想在事件中传递参数,则应该写一个子类继承Event,然后把传递的参数作为属性添加到event上。

这个类也有静态方法。

源码解析:

继承Disposable。有三个属性:type,target,currentTarget.

1,构造函数Event(type, opt_target):传入两个参数:type,事件类型,一个字符串; target,抛出事件的实例,就是绑定了listener的实例,target被保存在this.target中,同时this.currentTarget = this.target。

注:target和currentTarget的区别,target是触发事件的源,而currentTarget是捕获事件的对象。例如:a包含在b中,在b上注册了事件监听,点击a后,冒泡到b,则target=a,而currenttarget=b。

2,returnValue_  和 propagationStopped_两个属性,分别代表了 是否阻止默认事件 和是否阻止事件传播,returnValue_默认为true,propagationStopped_默认为false,也就是默认情况下不阻止默认事件,也不阻止事件传播。

3,对应的有preventDefault和stopPropagation方法来设置上述的两个值,以代表阻止默认事件或者阻止事件传播。

4,类Event有两个相同的静态方法 preventDefault和stopPropagation。实现和上述两个实例方法一样的效果。


EventType:一个常量,定义了默认的事件类型。包括:鼠标,键盘,表单,music,复制粘贴,html5等事件。

EventWrapper:接口,包含listen和unlisten两个方法。

BrowserEvent:继承自Event,除type,target,currentTarget之外,还包含了大量相关的属性:坐标,键盘状态,鼠标状态等。

Listener: 保存一个Listener的信息,注意,这个Listener和listen中传入的listener的区别,这个是对象,那个是函数,并且是包含关系:

listener: 回调函数,这个就是listen中传入的listener函数,别和Listener类的实例搞混了。

proxy:事件代理,用来捕获浏览器触发的事件,并触发对应listener。比如注册了a的click事件,则proxy捕获click事件,并调用注册的回调函数。

src:

type

capture

handler:这个就是回调函数中的this,如果未定义,则用src

callOnce

key:一个自增的id

handleEvent(e): 处理事件的函数,如果listener是函数,则调用listener.call(this,e), 否则调用listener.handleEvent(this, e);

EventWrapper:一个接口,只声明了listen和unlisten函数,没有具体实现。

这个接口意义是什么?可以在listen和unlisten中定义自己的行为?

EventTarget:所有事件源的父类,实现了这个接口的类可以有事件源的特征:注册监听,触发事件。因此,如果一个组件可以在其上添加事件监听,并且其可以触发事件,则其应该继承eventTarget。

一般来讲,一个EventTarget类应该有一个对应的EventType属性,用来定义此类所有的事件类型。

注意不用的时候,随时销毁listener,因为goog.events保存有所有listener的引用,所以不会被垃圾回收器自动回收。

继承子Disposable.

customEvent_ = true;表示这是一个用户定义的event target,区别于domevent,在listen()时不会调用addEventListener来添加对浏览器api的代理。

parentEventTarget_/getter/setter:

addEventListener/removeEventListener: 调用goog.events.listen/unlisten(this,xxxxx);唯一的区别就是把src指向了自己

dispatchEvent:调用goog.event.dispatchEvent(this,e);同上,唯一的区别是把src指向自己




goog.events: 重要!

 一个实例对象(不是一个类),提供了事件处理的全部逻辑,从注册监听,到抛出事件,到捕获事件并触发回调函数。通过一个map来分类存储listener,通过listener.ky来唯一标示一个listener。如果重复注册(src,type,listener,capt都相同),则只返回listener.key。注意dom事件和custom事件的区别。如果是dom事件,其抛出事件是浏览器做的,而不是通过dispatch做的。对于dom事件,listen函数中会同时生成一个proxy来代理。

重要方法说明:

goog.events.listen(src,type,listener, opt_capt, opt_handler):

参数说明:

src:事件源,应该是个实现了EventTarget接口的实例,也可以是一个dom元素(dom元素本身就是EventTarget类型的,也有自己的dispatchEvent方法,只是各个浏览器的实现标准不同)。

type:事件类型,一个字符串,为了跨浏览器,最好使用goog.events.EventType中定义的事件类型,或者各个组件中定义好的事件类型。

listener:可以是一个函数或者是一个EventHandler实例,如果是一个函数,则触发事件时会调用这个函数。

其中的this:

1,如果src是一个EventHandler,则触发事件时调用eventhandler.handlerEvent方法,this就是event handler。

2,否则,this值就是opt_handler,

3,如果opt_handler也未定义,则this是src。

opt_capt:是否在捕获阶段触发事件,默认false,即默认在冒泡阶段触发事件

opt_handler:回调函数中的this,如果回调函数是一个EventHandler对象 则不需要此参数。

此方法用于绑定事件监听,最常用的方法。



源码解析:

listeners_:根据listener.key来分别存储每一个listener。这样可以根据key快速查找到listener。

listenersTree_: 可以看做一个三层tree,第一层保存了各种type,第二层,每个type下根据cap来分为true和false两个分支,第三层,根据srcId,最后分支为叶节点,叶节点下面保存的是注册到这个src下的所有listeners数组。

其结构如下:listenersTree_[eventtype][capt][srcid] ,下面的每一项是一个listenerArray,里面是一个个的listener。

sources_:根据listener.srcId来存储每一个listener

listen(src,type,listener, opt_capt, opt_handler):重点!注册事件监听。将一个listener添加到其在listenerTree_中对应的位置,如果路径不存在 则创建,如果重复添加,则返回listener.key。同时会注册一个proxy,这个proxy会调用对应的listener。所以,如果对一个<a>注册了一个监听,回调函数是fun,则点击此标签,并不是直接调用f,而是调用了f对应的proxy,proxy做一定的处理之后再调用fun,好处是在proxy中可以做一定的处理而屏蔽浏览器之间的差异。handler就是回调函数中的this。

参数说明:其中src是事件源,type是事件类型,listener是EventHandler类的实例或者是函数,opt_capt是 是否在捕获阶段触发。opt_handler:如果listener是函数,则handler是函数中的this,如果listener是一个EventHandler实例,则handler没有作用,因为回调函数中是listener.handleEvent,其this被指定为listener自身(this.listener.handleEvent.call(this.listener, e)。

listenOnce():调用listen,只是将listener.listenOnce = true;

[un]listenWidthWrapper();调用wrapper.[un]listen,参见wrapper中问题:有什么意义?

unlisten():在listenersTree_中找到listener,然后调用unlistenByKey(lietener.key);

unlistenByKey():根据key,在listeners_,listenersTree_和sources_中分别将此listener移除。在listenerTree_中移除是通过调用cleanUp_来完成的

cleanUp_清理一个listener数组。如果清理完后为空,则从listenerTree_删除此数组。

removeAll(opt_obj,opt_type,opt_capt):移除obj上的所有listener,如果obj!=null,则在sources_中根据srcId找到listeners,否则,在sources_中通过匹配type和capt来找到listeners,最终都是调用unlistenByKey来完成。

getListeners -> getListeners_(obj,type,capt),根据三个参数在listenersTree_中找出listeners。listenersTree_叶节点本来存在就是满足type&cap&src的listeners数组。

getListener(src,type,listener,opt_cap,opt_handler):同理,在listenersTree_中找到符合的Listener并返回。

hasListener(obj,opt_type,opt_cap):

expose:

fireListeners(obj,type,cap,eventObject):触发符合前三个参数的event,并且将eventObject当做参数传给回调函数。实现:在listenerTree_中找到listeners数组,然后逐个调用fireListener。

fireListener(listener, eventObject):直接调用listener.handleEvent(obj),达到的效果就是触发事件,并将obj作为参数传递给回调函数。

dispatchEvent(src, e):重点!触发一个事件。在src上触发一个事件,此函数主要做了三件事:

1,包装e,如果e是一个字符串,则创建一个Event(e, src),如果e是一个object但是不是Event类型,则创建一个Event继承e,否则,只设置e.target=e.target || src。

2,处理捕获阶段:从src向上查找其parent,并push到ancestors中,然后从length-1 -> 0诸葛触发parent上的对应事件。并将e.currentTarget = parent. e.target 总是固定的。

3,处理冒泡阶段:同捕获阶段,不同的是顺序相反:从0 -> length-1逐个触发parent上的对应事件。

捕获阶段和冒泡阶段触发事件都是通过fireListener_来完成的。

handleBrowerEvent_:捕获浏览器触发的事件,并调用对应的listener。proxy就是通过此函数来实现其功能的。因为proxy调用了proxyCallbackFunction,而在events.js中将proxyCallbackfunction  = handleBrowerEvent。前面已经说过,注册监听的时候,如果不是customEvent,则同时生成一个proxy并将其注册给浏览器事件,这个proxy调用了handleBrowerEvent,handleBrowerEvent对event进行包装,生成一个browerEvent对象,并通过fireListener调用对应的listener并将browerEvent传递给回调函数。


InputHandler实现原理-通过包装原有事件类型实现新的事件类型:

InputHandler实际上是通过包装原有事件生成新的事件类型,这是一个通用的实现思路:

inputhandler实际上是以原有的事件为基础,通过包装生成自己的事件。主要包括如下两步:

1,在构造函数中传入被监听的对象element,然后监听可能会改变element的内容的事件:oninput || [keydown,paste,cut,drop](因为onpinut是新的标准,如果浏览器不支持,就通过keydown等事件来模拟),回调函数为this。因为如果回调函数不是函数类型会自动调用其handleEvent方法。

 91 this.eventHandler_.listen(

 92      this.element_,

 93       this.inputEventEmulation_ ? ['keydown', 'paste', 'cut', 'drop'] : 'input',

 94       this);

2,之后,当上述事件发生后,会调用this.handleEvent函数。在其中进行如下处理:

 如果根据e的类型过滤,如果此event没有改变内容,比如按下ctrl键,则直接return。否则,表明内容已经被上述事件改变,则把生成一个自己的event对象(其实就是e.target = this.element_;e.type = 自己定义的type类型)。并通过dispatch方法在this.element_上抛出这个事件。


所以,InputHandler有三个重要属性/方法:

构造函数 InputHandler(element):传入被监听的dom对象

eventtype:自定义的事件类型

handEvent:事件的代理,包装一个新的事件并抛出

原创粉丝点击