在 Forge - Dojo 事件和 Ajax
上个月,我们开始研究 Dojo,它是过去一两年中最流行的开源 JavaScript 工具包之一。虽然如果您想在 Web 应用程序中包含 Ajax 或复杂客户端功能,则不是必须使用工具包,但它肯定会使事情变得容易得多。特别是,此类工具包通常知道如何处理不同浏览器上 JavaScript 实现中的许多细微差异。JavaScript 的标准化程度比过去更高,但在尝试使用多个平台时仍然存在许多陷阱,使用工具包可以减轻您自己处理这些陷阱的负担。
在上个月的文章中,我们研究了 Dojo 的打包系统、它对 JavaScript 语言的一些增强功能以及其富文本编辑器。本月,我们将研究 Dojo 的其他一些可能引起现代 Web 开发人员兴趣的功能,包括对事件和 Ajax 的支持。
JavaScript 编程的基石之一是事件处理程序的使用——在特定事件发生时调用的函数。例如,我们可以定义一个打开警报框的函数
<script type="text/javascript"> function openAlert() { alert("Hello! This is an alert!"); } </script>
然后,我们可以告诉用户的浏览器,每当有人单击文本段落时,都调用我们的 openAlert 函数
<p onclick="openAlert();">This is a paragraph.</p>
在这个简短的示例中,有几个有趣的事情需要注意。首先,我们在本例中设置了 onclick 事件处理程序。大约还有六个其他的事件处理程序可供我们选择。在许多情况下,我们可能会设置多个事件处理程序。这在 CSS 出现之前的日子里尤其普遍,当时 JavaScript 事件处理程序用于在鼠标悬停在图标上时更改图标的外观。
其次,事件处理程序有时可以在您可能意想不到的上下文中使用。例如,上面的 <p> 标签有一个 onclick 处理程序。您通常不会想到单击文本段落,但我们可以这样做。这是现代拖放事件的基础。
第三,尽管 JavaScript 确实可以很容易地将处理程序附加到特定事件,但仍然涉及到一些混乱。我们无法轻松定义多个事件处理程序或断开已定义的处理程序的连接。
至此,您可能想知道 JavaScript 事件处理程序与使用 Dojo 进行 Ajax 和现代 Web 应用程序开发有什么关系。答案是,Dojo 的许多功能,跨越其所有软件包,都依赖于事件系统。例如,如果您想使用 Dojo 的 Ajax 包,则需要使用 Dojo 事件连接它。乍一看,这似乎具有限制性;但是,Dojo 事件非常容易理解。
作为一个简单的示例,让我们看看如何使用 Dojo 事件来实现我们之前的 onclick 处理程序。首先,我们需要修改我们的事件处理函数,使其接受一个参数,即事件本身
<script type="text/javascript"> function openAlert(evt) { alert("Hello! This is an alert from Dojo!"); } </script>
接下来,我们必须将段落连接到事件。我们没有直接设置 onconnect 处理程序,而是为我们的段落提供了一个 id 标签
<p id="para">This is a paragraph.</p>
现在,我们可以使用 Dojo 的 dojo.byId 函数——在某些方面类似于 Prototype 的 $() 函数——来获取节点本身
var para = dojo.byId("para");
最后,我们将我们的段落连接到我们创建的处理程序函数
dojo.event.connect(para, "onclick", openAlert);
如果我们把它们放在一起,我们就会得到清单 1 中显示的程序,我将其称为 test-dojo.html。
清单 1. test-dojo.html
<html> <head> <title>Testing JavaScript with Dojo</title> <script type="text/javascript" src="/javascript/dojo.js"></script> <script type="text/javascript"> dojo.require("dojo.event.*"); function openAlert(evt) { alert("Hello! This is an alert from Dojo!"); } </script> </head> <body> <p id="para">This is a paragraph.</p> <script type="text/javascript"> var para = dojo.byId("para"); dojo.event.connect(para, "onclick", openAlert); </script> </body> </html>
您可能注意到的一件事是文件中的三个 <script> 标签。第一个标签位于文档的 head 中,从服务器下载 dojo.js,即 Dojo 的主源文件。第二个标签也位于文档的 head 中,导入 Dojo 事件包并定义我们的事件处理函数 openAlert。第三个也是最后一个 JavaScript 代码段,它将节点附加到事件,位于文档的 body 中,紧接在我们定义的 p 标签之后。这是因为我们只能为已存在的对象设置事件处理程序,这意味着在 p 标签本身之后。
如果您将页面加载到浏览器窗口中,您将看到它的工作方式与之前的版本完全相同。鉴于此版本更复杂,它更好在哪里可能并不明显。
这里有一个例子。假设您想在事件处理程序中调用一个特定的对象方法,而不是简单的函数调用。JavaScript 使得直接从事件处理程序执行此操作变得困难。但是,dojo.event.connect 在其四参数版本中可以非常简单地处理此问题。与之前一样,前两个参数是节点和将触发处理程序的事件。第三个和第四个参数是要调用的对象和函数。例如
dojo.event.connect(eventObject, "onclick", handlerObject, "handlerMethod");
Dojo 还允许将多个处理程序连接到一个事件。在非 Dojo JavaScript 中,您只能通过使您的事件处理程序成为一个函数,然后调用其他函数来实现此目的。使用 Dojo 事件,您可以连接任意数量的方法
dojo.event.connect(para, "onclick", testFormContents); dojo.event.connect(para, "onclick", submitFormContents);
事件按照它们连接的顺序触发。因此,在上面的示例中,testFormContents 将在 submitFormContents 之前调用。
请注意,Dojo 允许您根据需要添加两次相同的事件处理程序。因此,请注意仅对每个事件处理程序组合调用一次 dojo.event.connect,以避免潜在的奇怪且难以调试的问题。
假设您想为您的用户提供专家模式,以便他们不必看到我们生成的所有烦人的警报框。我们可以创建一个按钮,当按下该按钮时,从对象中删除事件处理程序——哦,但是现在这变得有点棘手了,尤其是在我们需要处理多个事件的情况下。
解决方案是使用 dojo.event.disconnect,它的作用正如您可能预期的那样
dojo.event.disconnect(para, "onclick", testFormContents);
dojo.event.disconnect 要求参数与 dojo.event.connect 中使用的参数完全相同。但是,一旦调用它,事件就会断开连接。
事件系统的一个高级部分称为 advice,这个术语总是让我感到困惑,但在 Lisp 和面向方面的编程世界中很常见。advice 背后的基本思想是,您可以告诉系统在另一个函数之前或之后调用一个函数。(如果您使用过 Ruby on Rails,这类似于过滤器。)这无疑是一项高级功能,但它可能有助于调试应用程序——您可以简单地向函数添加 advice,在调用函数之前或之后调用记录器,而不是手动将日志语句插入到有问题的函数中。
Dojo 事件甚至有一个主题机制,可让您为事件通知创建多个通道。(这在某些方面类似于 Linux 和 UNIX 中的 syslog 工具。)因此,当另一个对象上发生特定事件时,特定对象可能会注册其兴趣。
最后,Dojo 事件用于为小部件提供功能——小部件是 Dojo 对由 HTML 和 CSS 组成的 GUI 元素的命名。
现在我们了解了如何创建和使用 Dojo 事件,我们可以看看如何使用 Dojo 执行 Ajax 查询。您可能还记得,Ajax(代表 Asynchronous JavaScript and XML,异步 JavaScript 和 XML)是一种 Web 开发范例,它利用浏览器在后台发出 HTTP 请求的能力。将此类后台 HTTP 请求与 JavaScript、DOM 和 CSS 结合使用,可以创建更直观和美观的 Web 应用程序。我们可以在没有 Dojo 或其他工具包的情况下创建 Ajax 应用程序,但是使用工具包会更容易更具表现力,仅仅是因为这意味着我们可以避免浏览器差异和不兼容性。
清单 2 显示了 dojo-ajax.html,这是一个仅包含一个标记为“Press here”(按此处)的按钮的页面。当按下按钮时,用户会看到一个警报框,就像清单 1 中一样。但是,在这个程序版本中,警报框的内容来自服务器端程序,在本例中定义为非常短的 hello.php(清单 3)。
清单 2. dojo-ajax.html
<html> <head> <title>Testing Ajax with Dojo</title> <script type="text/javascript" src="/javascript/dojo.js"></script> <script type="text/javascript"> dojo.require("dojo.io.*"); dojo.require("dojo.event.*"); function ajaxAlert(evt) { var ajaxArgs = { url: "hello.php", error: function(type, data, evt){ alert("An error occurred"); }, load: function(type, data, evt){ alert(data); }, mimetype: "text/plain", formNode: document.getElementById("myForm") }; dojo.io.bind(ajaxArgs); } </script> </head> <body> <form id="theForm"> <input type="button" id="theButton" value="Press here" /> </form> <script type="text/javascript"> var theButton = dojo.byId("theButton"); dojo.event.connect(theButton, "onclick", ajaxAlert); </script> </body> </html>
清单 3. hello.php
<? echo "Hello from the server!"; ?>
按钮本身被定义为我们可能对任何希望附加事件的按钮所做的那样,带有一个 id 属性。它位于一个名为“theForm”的非常小的 HTML 表单内
<form id="theForm"> <input type="button" id="theButton" value="Press here" /> </form>
使用 Dojo 事件,我们将按钮连接到一个函数 (ajaxAlert)
<script type="text/javascript"> var theButton = dojo.byId("theButton"); dojo.event.connect(theButton, "onclick", ajaxAlert); </script>
唯一剩下的问题是 ajaxAlert 函数的作用是什么。首先,它创建一个 JavaScript 对象字面量,并将其分配给局部变量 ajaxArgs。此对象字面量分配了几个名称,这些名称将有助于我们的 Ajax 调用工作:url 是服务器端程序的 URL,该程序将响应我们的 Ajax 调用,error 指示如果发生错误应调用哪个函数,load 指示如果对 url 的调用成功应调用哪个函数,mimetype 定义了我们期望从服务器接收的 MIME 类型。
其他一些 JavaScript 工具包的一个令人讨厌的方面是,它们要求您创建自己的名称-值对列表,以便在 Ajax 请求中提交。Dojo 不是这种情况。通过将对象字面量中的 formNode 名称设置为表单节点,我们可以确保所有表单元素都将传递到服务器。在这种特殊情况下,这不一定有用或有趣,但它肯定节省了一些程序员的时间并提高了程序的可读性。
最后,我们的 ajaxArgs 对象被绑定,我们开始运行了。单击按钮意味着调用关联的 Dojo 事件,即 ajaxAlert。该函数,由于 dojo.io.bind 的存在,然后将其参数发送到定义的 URL,并在成功完成后调用 load 函数。这非常简单,并为在应用程序中使用 Ajax 开辟了许多可能的途径。
Dojo,我们在本专栏的过去两期中探讨了它,以及 Prototype,我们在 2007 年 1 月刊中研究了它,都是 Web 开发人员希望提高其 JavaScript 质量的强大库。每个库都有与之相关的不同风格。我倾向于成为 Prototype 类型的人,但 Dojo 的许多方面也对我很有吸引力。特别是,Dojo 广泛的小部件集,以及这些小部件可以通过事件系统相互连接的方式,提供了一组丰富的功能,所有 JavaScript 开发人员都可以享受。即使您不打算使用 Dojo,也应该考虑安装并试用它,只是为了了解它的工作原理。
资源
关于 Dojo 的信息以及 Dojo 软件版本的主要来源是 dojotoolkit.org。该工具包的文档仍然有点稀疏,但在过去几个月中已得到显着改进,并且鉴于 Dojo 日益普及,持续改进似乎很有可能。Dojo 文档的主要 URL 是 dojotoolkit.org/docs,而 Dojo.book(基于 Wiki 的 Dojo 文档)位于 manual.dojotoolkit.org/index.html。
一些关于 JavaScript 工具包(包括 Dojo)的优秀文章位于 www.sitepoint.com/article/javascript-library。
最后,一个值得注意的 Dojo 事件介绍位于 www.dojotoolkit.org/docs/dojo_event_system.html。
Reuven M. Lerner,一位长期的 Web/数据库顾问,是伊利诺伊州埃文斯顿西北大学学习科学专业的博士候选人。他目前与妻子和三个孩子住在伊利诺伊州斯科基。您可以在 altneuland.lerner.co.il 阅读他的博客。