首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

4.3.3  JavaScript中实现灵活的事件模型

由于新W3C事件模型之间的不兼容性,创造灵活的事件监听器框架的目标仍然没有达到。我们在第3章中描述了Observer模式,看起来很适合这个目标,它允许我们以一种灵活的方式向事件源添加和删除观察者。很清楚,W3C感受到了同样的事情,因为修改过的事件模型使用了Observer模式,但是浏览器厂家所交付的却是不一致的且错误的实现。传统的事件模型比Observer模式要差很多,但是或许可以使用一些自己编写的代码来增强它。

1. 管理多个事件回调

在实现我们自己的解决方案之前,我们先通过一个简单的例子来了解问题所在。代码清单4-7显示了一个简单的Web页面,其中一个大的DIV区域以两种方式响应鼠标的移动事件。

代码清单4-7  mousemat.html


首先,它在writeStatus()函数中更新了浏览器的状态条,然后在drawThumbnail()函数中通过在旁边小的缩略视图区域中重新定位一个点,更新自己在这个区域的映像,以此来复制鼠标光标位置的移动。图4-6显示了活动中的页面。

这两个行为是彼此独立的,我们希望能够将这些行为和鼠标移动的其他响应进行交换,即使是在程序运行时。

4-6  mousemat程序在“虚拟mousemat”主区域以两种方式追踪鼠标移动事件:以鼠标的坐标更新浏览器下方的状态条;在缩略视图上随着鼠标光标同步移动的点

mouseObserver()函数是事件监听器(顺便说一下,第一行执行了一点简单的跨浏览器魔法。与MozillaOpera或者Safari不同,IE不向回调处理函数传递任何参数,而是将Event对象保存在window.event中)。在这个例子中,我们在事件处理函数中依次调用writeStatus()drawThumbnail()将两种活动硬连接在一起。程序准确地完成了我们希望它做的事情,并且因为这只是一个小程序,mouseObserver()代码还算清晰。在理想情况下,我们希望使用一种更加清晰的方式来将事件监听器连接在一起,以便可以扩展到更加复杂或动态的情况

2. JavaScript实现观察者

建议的解决方案是定义一个通用的事件路由器对象,它为目标元素附加一个标准函数,作为一个事件回调,并且维护一个监听器函数的列表。这允许我们以下面的方式重写mousemat的初始化代码:

window.onload=function(){

  var mat=document.getElementById('mousemat');

  ...

  var mouseRouter=new jsEvent.EventRouter(mat,"onmousemove");

  mouseRouter.addListener(writeStatus);

  mouseRouter.addListener(drawThumbnail);

}

我们定义了一个EventRouter对象,传入DOM元素并希望注册为参数的事件类型。然后向路由器对象增加监听器函数,路由器对象也支持removeListener()方法,这里我们不需要该方法。这个对象看起来很直接,但是我们如何实现它呢?

首先,我们为对象编写一个构造函数,这在JavaScript中仅仅是一个函数(附录B包含了JavaScript对象语法的初级教程。如果不明白下面的代码,可以参考该教程)。

jsEvent.EventRouter=function(el,eventType){

  this.lsnrs=new Array();

  this.el=el;

  el.eventRouter=this;

  el[eventType]=jsEvent.EventRouter.callback;

}

我们定义了监听器函数的数组(最初它是空的),保存了一个到DOM元素的引用,并且使用3.5.1节描述的模式给DOM元素添加了一个到这个对象的引用。然后我们分配一个EventRouter类的静态函数,简单地称作callback,作为事件处理函数。记住在JavaScript中,方括号和点记号是等同的,这意味着:

el.onmouseover

el['onmouseover']

是相同的。为使用方便,我们将属性名称作为参数传递进来。这与Java或者.NET语言的反射是类似的。

然后,让我们看看callback

jsEvent.EventRouter.callback=function(event){

  var e=event || window.event;

  var router=this.eventRouter;

  router.notify(e)

}

因为这是一个回调函数,函数上下文是触发事件的DOM节点,而不是路由器对象。我们使用前面提到的后端对象模式得到已经附加在 DOM 节点上的EventRouter的引用,然后调用路由器的notify()方法,将事件对象作为参数传递进来。

EventRouter对象的完整代码如代码清单4-8所示。

代码清单4-8  EventRouter.js


注意,数组的一些方法不是标准的JavaScript,而是在我们扩展过的数组定义中定义的方法,附录B中讨论了这些方法。特别地,addListener()removeListener()可以通过使用append()remove()方法容易地实现。监听器函数使用Function.call()方法来调用,它的第一个参数是函数上下文,后续的参数(在这里是事件)传递给被调用的函数。

已修改的mousemat例子如代码清单4-9所示。

代码清单4-9  已修改的mousemat.html,使用EventRouter


内嵌的JavaScript是非常简单的。所需要做的只是创建EventRouter传进监听器函数,并且为监听器提供实现。我们将此留给读者作为练习,包括使用选择框来动态添加和删除每个监听器。

在此我们讨论了Ajax应用中的控制器层,以及设计模式特别是Observer模式可以扮演的角色,即用来保持代码清晰且易于开发。在下一节中,我们将查看MVC模式的最后一部分——模型。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论