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


![]()
首先,它在writeStatus()函数中更新了浏览器的状态条,然后在drawThumbnail()函数中通过在旁边小的缩略视图区域中重新定位一个点,更新自己在这个区域的映像,以此来复制鼠标光标位置的移动。图4-6显示了活动中的页面。
这两个行为是彼此独立的,我们希望能够将这些行为和鼠标移动的其他响应进行交换,即使是在程序运行时。

图4-6 mousemat程序在“虚拟mousemat”主区域以两种方式追踪鼠标移动事件:以鼠标的坐标更新浏览器下方的状态条;在缩略视图上随着鼠标光标同步移动的点
mouseObserver()函数是事件监听器(顺便说一下,第一行执行了一点简单的跨浏览器魔法。与Mozilla、Opera或者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元素的引用,并且使用
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模式的最后一部分——模型。







