Chinaunix首页 | 论坛 | 博客
  • 博客访问: 384081
  • 博文数量: 69
  • 博客积分: 1984
  • 博客等级: 上尉
  • 技术积分: 953
  • 用 户 组: 普通用户
  • 注册时间: 2007-03-28 00:43
个人简介

学无所长,一事无成

文章分类

全部博文(69)

文章存档

2015年(19)

2014年(14)

2013年(9)

2012年(17)

2010年(10)

我的朋友

分类: JavaScript

2014-07-07 15:42:47

原文:http://dojotoolkit.org/documentation/tutorials/1.10/events/
难度等级:初级  Dojo 版本:1.10

Events with Dojo


本教程中,我们会探讨 dojo/on 以及 Dojo 如何使用它来简化 DOM events 关联。我们还会探索 Dojo 的 publish/subscribe (发布/订阅)框架: dojo/topic。


起步

你的JavaScript 代码大部分应该都是用来处理事件的:响应或者产生新的事件。这也意味着一个事实,创建响应式的,交互式的 web应用核心就在于创建有效的事件关联。事件连接(event connections)让你的应用响应用户交互,等待动作发生。Dojo 主要的 DOM 事件机制就是依赖 dojo/on;让我们学习如何使用这个 module!

DOM 事件

你可能经常问自己,“DOM 是不是还没有一套完备的机制用来注册事件句柄?”答案是 yes,因为并不是所有的浏览器都遵循 W3C DOM 规范。来自于主流浏览器厂家的 DOM 实现主要采用三种方法注册事件句柄: addEventListener, attachEvent, 和 DOM 0。除此外还有两种不同的 event object 实现,至少一种浏览器有  和  问题(大家都知道是谁吧)。Dojo 改进了这些 DOM events 方法,消除了各种 native APIs 的差异,阻止了内存泄露,并将其封装成唯一一个简明的事件 API,就是  dojo/on。

假定我们有如下标记:

  1. <button id="myButton">Click me!</button>
  2. <div id="myDiv">Hover over me!</div>


我们再想象这个功能,点击 button 后,div 变成蓝色,鼠标滑过则变成红色,鼠标离开恢复成白色。dojo/on 实现这个功能就非常简单: 
  1. require(["dojo/on", "dojo/dom", "dojo/dom-style", "dojo/mouse", "dojo/domReady!"], 
  2.     function(on, dom, domStyle, mouse) {
  3.         var myButton = dom.byId("myButton"),
  4.             myDiv = dom.byId("myDiv");
  5.         on(myButton, "click", function(evt){
  6.             domStyle.set(myDiv, "backgroundColor", "blue");
  7.         });
  8.         on(myDiv, mouse.enter, function(evt){
  9.             domStyle.set(myDiv, "backgroundColor", "red");
  10.         });
  11.         on(myDiv, mouse.leave, function(evt){
  12.             domStyle.set(myDiv, "backgroundColor", "");
  13.         });
  14. })

注意这里我们还需要  dojo/mouse 模块。 因为并非所有浏览器都原生支持 mouseenter 和 mouseleave 事件,dojo/mouse 可以添加这个支持。你可以仿照 dojo/mouse 编写自己的模块,实现自定义事件类型。

这个例子展示了调用的通用模式:on(elementevent namehandler)。可理解为,将某元素上的某事件,绑定到某句柄。这个模式适用于all window, document, node, form, mouse, 和 keyboard 事件。

注意同老的  dojo.connect API 不同,使用 dojo/on 模块时,  "on"  这个event name 前缀必须忽略。

on 方法不仅标准化事件注册的 API,同时也标准化事件句柄的工作方式:

  • 事件句柄总是以其注册顺序被调用。
  • 他们被调用时,总是以  event object 为其 第一个参数。
  • 事件对象被标准化,包含通用的 W3C 事件对象属性,包括 target 属性,一个 stopPropagation 方法和一个 preventDefault 方法。

同  DOM API 不同, Dojo 还提供了移除事件句柄的方法:handle.remove。on 函数的返回值是一个简单对象,带一个 remove 方法,当其调用时会删除事件 listener。比如,你想要一个只运行一次的事件,可以如下编写:

  1. var handle = on(myButton, "click", function(evt){
  2.     // Remove this event using the handle
  3.     handle.remove();

  4.     // Do other stuff here that you only want to happen one time
  5.     alert("This alert will only happen one time.");
  6. });

 

顺便提下,dojo/on 包含了一个更便捷的方法来实现同样功能:on.once。其参数同 on 完全一样,并实现句柄在调用一次后自动移除。

最后还需注意:缺省情况下,on 是在第一个参数传入的 node 的上下文中运行 event handlers 。只有当 on 用于 event delegation(事件委派) 时例外,我们马上会讨论到这些。

你还可以使用 lang.hitch (来自 dojo/_base/lang 模块) 来指定 handler 运行的上下文。Hitching 同对象方法协作时非常有用

  1. require(["dojo/on", "dojo/dom", "dojo/_base/lang", "dojo/domReady!"], 
  2.     function(on, dom, lang) {
  3.         var myScopedButton1 = dom.byId("myScopedButton1"),
  4.             myScopedButton2 = dom.byId("myScopedButton2"),
  5.  
  6.             myObject = {
  7.                 id: "myObject",
  8.                 onClick: function(evt){
  9.                     alert("The scope of this handler is " + this.id);
  10.                 }
  11.             };

  12.         // This will alert "myScopedButton1"
  13.         on(myScopedButton1, "click", myObject.onClick);
  14.         // This will alert "myObject" rather than "myScopedButton2"
  15.         on(myScopedButton2, "click", lang.hitch(myObject, "onClick"));
  16. });

 

同  on 函数的前身 dojo.connect 不同, on 不会接受 handler 作用域以及方法等参数。如果你想保留运行上下文,你需要通过 lang.hitch 接收第三方参数。

NodeList 事件

前面其他文章提到过,NodeList 提供了给多个 nodes 注册事件的方法:on 方法。这个方法同 dojo/on 调用模式基本一样,除了没有第一个参数(因为 NodeList 中的 nodes 已经指明了 on 函数需要关联的对象,因此省略)。

dojo/query 中已经包含了 on 方法,因此想要使用 NodeList.on 的话也不用 require dojo/on。

让我们看一个更高端些的例子:

  1. <button id="button1" class="clickMe">Click me</button>
  2. <button id="button2" class="clickMeAlso">Click me also</button>
  3. <button id="button3" class="clickMe">Click me too</button>
  4. <button id="button4" class="clickMeAlso">Please click me</button>
  5. <script>
  6. require(["dojo/query", "dojo/_base/lang", "dojo/domReady!"],
  7.     function(query, lang) {

  8.         var myObject = {
  9.             id: "myObject",
  10.             onClick: function(evt){
  11.                 alert("The scope of this handler is " + this.id);
  12.             }
  13.         };
  14.         query(".clickMe").on("click", myObject.onClick);
  15.         query(".clickMeAlso").on("click", lang.hitch(myObject, "onClick"));

  16. });
  17. </script>
 

注意同 NodeList.connect 不同,NodeList.on 方法的返回值是一个 handles 组成的数组,而不是 NodeList 本身(可形成链式调用),这种方式便于后续对 handles 进行删除。这个返回数组包含了一个顶层的 remove 方法,可以一次性删除所有的 listeners。

事件委派-Event Delegation

如上面讨论所说,NodeList  的 on 方法可以很容易的对多个 DOM 节点的同一事件关联相同的 handler。dojo/on 使用一种效率更高的方法 event delegation(事件委派) 实现同一目的。

事件委派的核心思想就是:与其对每一个单独的节点各自关联一个事件 listener,不如在更高一层的节点上只关联一个 listener,这个 listener 捕获到事件后,会判断目标节点是否需要处理,如果需要,则执行相关逻辑。

在以前(现在也可以)这是通过针对 NodeList 的 dojox/NodeList/delegate 扩展来实现的。Dojo 1.10 版本中,使用 dojo/on 模块就可以实现了,语法格式 on(parent element, "selector:event name", handler)。

为更好地阐述,让我们看另外一个例子,所有条件同前面例子一样:

  1. <div id="parentDiv">
  2.     <button id="button1" class="clickMe">Click me</button>
  3.     <button id="button2" class="clickMe">Click me also</button>
  4.     <button id="button3" class="clickMe">Click me too</button>
  5.     <button id="button4" class="clickMe">Please click me</button>
  6. </div>
  7. <script>
  8. require(["dojo/on", "dojo/dom", "dojo/query", "dojo/domReady!"],
  9.     function(on, dom){

  10.         var myObject = {
  11.             id: "myObject",
  12.             onClick: function(evt){
  13.                 alert("The scope of this handler is " + this.id);
  14.             }
  15.         };
  16.         var div = dom.byId("parentDiv");
  17.         on(div, ".clickMe:click", myObject.onClick);

  18. });
  19. </script>

注意我们仍然需要 dojo/query 模块,但我们不再直接使用它。因为 dojo/on 需要使用 dojo/query 提供的选择器引擎来筛选节点,以实现事件委派。我们为什么不让 dojo/on 自动载入这个模块,这是为了尽量削减其调用,以避免不靠谱的程序员仅仅为了实现某些特性就将其一直占用。

运行这个 demo,我们会注意到 this 是指向我们需操作的节点上的,而非 parentDiv 节点。将 on 用于事件委派模式时:this 不再指向第一个参数传入的 node ,而是指向选择器匹配的 node 。一旦你真正理解了,这个特性会非常有用。

Object Methods

dojo/on 的前身 dojo.connect 可以实现一个功能,就是 function-to-function 事件关联(译注:就是可以在函数间实现关联,一个函数调用,会触发关联函数的调用。用于实现AOP 编程,如记录日志等动作。)。这个功能已经分离出去,放入一个独立的模块 dojo/aspect 中。我们很快就会介绍到 dojo/aspect 了!

发布/订阅-Publish/Subscribe

截止目前以上所有例子都有一个特点,我们需要有一个现成的对象作为事件载体,产生事件以及处理事件。但如果某个节点上没有句柄或者根本不知道某个 object 是否已经创建出来了,该怎么办?这就要讲到 Dojo 的发布/订阅(pub/sub)框架了,在 Dojo 1.10 版本中这是由 dojo/topic 模块提供的。pub/sub 允许你为某个主题 "topic"注册一个 handler (就是订阅这个 topic),一旦这个 "topic"被发布,handler 就会被调用(topic 就是你为事件起的名字,是一个字符串,它可以有多个发布源)。

我们设想一个正在开发中的程序,我们需要些 buttons 用来提醒用户某个 action 发生了;对通知提醒响应的方法我们只希望编写一次,而且我们也不想仅仅为了给 button 添加个小方法就封装个 object。pub/sub 很适合这种场景:

  1. <button id="alertButton">Alert the user</button>
  2. <button id="createAlert">Create another alert button</button>

  3. <script>
  4. require(["dojo/on", "dojo/topic", "dojo/dom-construct", "dojo/dom", "dojo/domReady!"],
  5.     function(on, topic, domConstruct, dom) {

  6.         var alertButton = dom.byId("alertButton"),
  7.             createAlert = dom.byId("createAlert");

  8.         on(alertButton, "click", function() {
  9.             // When this button is clicked,
  10.             // publish to the "alertUser" topic
  11.             topic.publish("alertUser", "I am alerting you.");
  12.         });

  13.         on(createAlert, "click", function(evt){
  14.             // Create another button
  15.             var anotherButton = domConstruct.create("button", {
  16.                 innerHTML: "Another alert button"
  17.             }, createAlert, "after");

  18.             // When the other button is clicked,
  19.             // publish to the "alertUser" topic
  20.             on(anotherButton, "click", function(evt){
  21.                 topic.publish("alertUser", "I am also alerting you.");
  22.             });
  23.         });

  24.         // Register the alerting routine with the "alertUser" topic.
  25.         topic.subscribe("alertUser", function(text){
  26.             alert(text);
  27.         });

  28. });
  29. </script>
 

这种事件模式有一个优点很实用,我们可以不用创建任何 DOM 对象,就可以 在单元测试中调用并测试 alerting routine (响应方法)了。事件处理方法,同事件的发生已经完全解耦了。

如果你要取消某个主题的关注, topic.subscribe 函数的返回值是个对象,带有一个 remove 方法,调用它就可以删除这个 handler。

注意同 dojo.publish 不同,topic.publish  发布消息时带的参数不需要压入一个数组。
比如
 topic.publish("someTopic", "foo", "bar") 同 dojo.publish("someTopic", ["foo", "bar"]) 就是等价的。

小结

Dojo 的事件系统非常强大,使用上又非常简单。on 方法还消除了不同浏览器间 DOM 事件的差异。Dojo 的 pub/sub 框架,dojo/topic提供给开发者一个手段,可以将事件处理器和事件发器生轻松解耦。花时间好好熟悉这些工具,这对开发 web 应用是非常值得的。


阅读(4015) | 评论(3) | 转发(0) |
给主人留下些什么吧!~~

hellosilent2014-07-30 22:14:20

博主,有个问题想向您请教一下:我用的是 jsonrest ,它的  remove 方法,删除一个 ID 为 55 的数据,结果 post 的 url  变成了 ; myurl.html/55 了; 而不是  myurl.html?id=55  ; 这个中情况怎么办呢?谢谢

zzge1632014-07-08 09:38:35

牛人自有牛人NB的地方,dojo用了好几个月了,从来没有认真翻译过这些文章,自惭形秽。博主,这是迄今为止最全的dojo翻译文档,千万不要停!