级别: 初级
Micah Dubinko, 首席顾问, Brain Attic, L.L.C.
2004 年 12 月 01 日
一
些标记技术涉及到为文档的某些部分附加上行为。XML Events 是 W3C
推荐的一种标准,它允许通过声明为特定的元素附加行为,这种行为可以是 XML
中预先定义的一组动作,也可以是更一般的脚本语言调用。本文将简要地介绍 XML Events 的由来、用途以及其工作方式。
现代的网站是高度交互的:动态的导航菜单、图像切换、表单,甚至还支持拖拉操作。这类网站的一个共同因素是通过某种技术将行为与文档的特定部分联系起来。不幸的是,当前的实践因为严重依赖脚本而造成了混乱,特别是那些需要适应多种浏览器的代码。
两种类型的事件
清单 1 列出了一些可能的变体。它定义了两个事件处理程序,一个用于
load
事件,另一个用于
unload
事件。一个处理程序是用 VBScript 编写的,通过一种不常见的技术附加到元素上,并用到了
script
元素的额外属性。另一个则是用 ECMAscript(请参阅
参考资料)编写的。
清单 1. 两种类型的事件
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Intrinsic Events</title> <script event="onload" for="window" language="vbscript"> <!-- alert "hello" --> </script> </head> <body onunload="alert('goodbye')"> </body> </html>
|
如果使用
Microsoft Internet Explorer 或者其他支持 VBScript
的浏览器查看,在加载文档的时候,就会出现一个显示“hello”的警告框。基本上所有的浏览器在关闭窗口或者导航到其他页面时都会显示
“goodbye”。要完整地描述这到底是怎么发生的,就需要了解 DOM Events,该标准目前由 Document Object Model
(DOM) Level 2 Events Specification(请参阅 参考资料)授权的 W3C 文档指定。
事件的工作原理
事件到底是什么呢?对于本文而言,最好不要从“事件”一词的传统含义上思考,而应将其看作一种数据结构,包含变化的某些事物的详细信息。比方说,在
mouseover
事件中,我们所关心的事件对象的信息是鼠标指针的坐标、按下了哪个键、当前是否按下了修饰键如“shift”等等。单个事件对象的存活期非常短,在其消失之前刚够时间进行即时处理。
每个事件都有一个
目标,XML 或 HTML 元素和事件的关系最密切。事件
处理程序是对应特定事件的一段可执行代码或者标记。某些事件和其他处理程序不同,比如单击一个超链接,会引发称为
默认动作的活动。
清单 1 中
unload
事件的目标是
body
元素,事件处理程序
onunload
属性中为数不多的那一点脚本。
事件流
最简单、最常用的技术是直接将事件处理程序附加到目标上,过去,浏览器只支持这种技术。但是,这样做常常行不通,比如清单 2 中的例子:
清单 2. 论证是否需要事件观测者
<p>You want to capture all clicks on this paragraph, even if the text has <em>special markup</em>.</p>
|
清单 2 中,假设对目标为
p
元素的
click
事件有一个处理程序。如果用户恰好单击“special markup”这两个词,则会创建目标为
em
元素的一个事件。因为目标不是段落,单击事件处理程序就不会被触发。解决这个问题的方法是在
p
元素上放一个
observer 事件,它能够对以该元素自身或者
它的所有子元素为目标的事件作出响应。
DOM Level 2 Events 描述了如何连接事件处理程序,以解决
清单 2 中所示的问题,尽管由于浏览器的历史原因,这种方法可能有点过于复杂(Internet Explorer 和 Netscape 的早期版本各有自己的特点,其中一些要比正式的 DOM 规范早得多)。事件按照文档的树状结构进行假想的旅行,这个过程称为
传播。实际上它的旅行有两次:第一次旅行称为
捕获,从文档根开始,前进到目标元素。进行常规的目标处理后,第二次旅行称为
冒泡(bubbling),从目标元素开始,再回到文档根。每个阶段中,路径中的任何元素都可以注册为事件观测者,从而能够触发事件处理程序。然后事件将停止传播,避免后面的观测者再检测到发生的事件。图 1 说明了传播的过程。
|
事件和可访问性
无论何时考虑事件,都需要判断您到底需要哪种事件。比方说,确实关心按钮是否被鼠标单击吗?如果用其他某种方式激活又怎么样?一些 Web 用户没有鼠标,甚至都没有这样的图形用户界面。更好的办法是尽量使用设备无关的事件,比如用
DOMActivate 代替按钮的
click 事件。
W3C 发布的一些文档(请参阅
参考资料)描述了增强 Web 内容访问性的技术。
|
|
图 1. 事件的传播
如果需要设置事件观测者,应该使用捕获阶段还是冒泡阶段呢?下面是一些指导原则:
- 如果处理单个观测点 —— 这也是最常见的情况,使用哪个阶段都行,两者没有实际区别。只有需要从文档树中的多个位置观测一个事件时才会造成区别。
- 某些事件(如
focus
)不参与冒泡阶段,因此只能在捕获阶段观测,或者直接在目标上观测。
- 当存在多个观测者时,如果希望首先触发最近的观测者则使用冒泡阶段。比如,假设在
图 1 中,
body
和
p
上都有观测者。捕获的时候
body
上的观测者将首先被触发,然后再激活
p
上的观测者。而冒泡阶段,将首先激活
p
上的观测者,然后才是
body
上的观测者。
- 要记住,对于
清单 1 中
onunload
所用的 HTML 4.0 风格的语法,观测者被注册到冒泡阶段。
- 无论使用冒泡阶段还是捕获阶段,默认动作总是在所有的传播完成后执行。停止事件传播本身不会禁止默认动作的执行。一个单独的 API 特性允许取消默认动作,无论传播过程中发生什么事。
DOM Events 规范定义了从脚本中附加事件观测者的方法,如清单 3 所示。
清单 3. 附加事件观测者
var el = document.getElementById('observer_element_id'); el.addEventListener("mouseover", highlight_func, true); el.addEventListener("mouseout", normal_func, true);
|
addEventListener
调用中的第一个参数是事件名,第二个参数是要执行的函数引用,第三个参数是
true
(表示在捕获阶段)或者
false
(表示在冒泡阶段)。DOM Events 规范定义了一些核心事件,以及停止事件传播和取消关联的默认动作的 API 方法。
需要注意的是,这些 API 的不同浏览器的实现有很大的不同。具体地说,Internet Explorer 直到 6.0 版还不支持
addEventListener
,但它使用了名为
attachEvent
的一个类似函数。
从事件到 XML Events
到目前为止,所有和事件有关的一切都和过程性脚本联系在一起。XML Events 规范在 DOM Level 2 Events 的基础上,增加了连接事件观测者的声明性方法。W3C XForms 规范(请参阅
参考资料)是率先定义兼容 XML Events 的元素库的标准之一,该元素库可以通过声明完成一般性的任务。清单 4 给出了 XForms 中的一个例子。(关于其中使用的完整名称空间声明,请参考
清单 5 中的完整例子。)
清单 4. 声明性事件处理程序
<xf:trigger> <xf:label>Reset the form</xf:label> <xf:reset ev:event="DOMActivate" model="mymodel" /> </xf:trigger>
|
XML Events 被定义成一些属性。
清单 4 中的
ev:event
属性规定了要监听的特定事件,处理程序是
xf:reset
元素,观测者被默认为父元素
xf:trigger
。注意,该清单中根本没有脚本。
XML Events 规范主要由可以放置在现有元素中的属性定义组成,还有一个
listener
元素用来容纳这些属性。每个属性都映射为 DOM Level 2 事件的一个特性。表 1 列出了所有的 XML Events 属性。
表 1. XML Events 属性
属性
|
功能
|
event
|
如
清单 4 所示,这个必需的属性用于命名触发监听者的事件
|
observer
|
该属性指向作为观测者的元素的惟一 ID |
handler
|
该属性指向执行某种动作或处理的元素的 URI,该元素可能在不同的文档中 |
phase
|
capture 或
default ,该属性规定使用捕获阶段
|
propagate
|
stop 或
continue (默认值),该属性规定事件是否继续传播
|
defaultAction
|
cancel 或
perform (默认值),该属性规定传播后是否激活默认动作
|
target
|
该属性要求监听者
仅 对指向特定目标的事件作出响应,并且仅在特殊情况下使用
|
id
|
该属性为
listener 元素赋予文档中惟一的标识符
|
简便的默认值
清单 4 说明了用最少的属性使用 XML Events 的便捷方法:XML Events 属性直接附加到处理程序元素上。如果省略
ev:handler
属性,拥有 XML Events 属性的元素就被看作是处理程序。观测者可以通过
ev:observer
属性指定,或者默认为父元素。在
清单 4 中,观测者为
xf:trigger
。
另一种类型的默认允许将 XML Events 属性附加到观测者元素上,这种情况下没有
ev:observer
属性,但是出现了
ev:handler
属性。
清单 5 重新创建了
清单 1 中的行为,显示了这两种默认技术,并使用了脚本和声明性动作。
清单 5. 使用 XML Events 的两种方法
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xf="http://www.w3.org/2002/xforms"> <head> <title>XML Events</title> <script type="text/javascript" ev:event="load" ev:observer="bod_id"> alert("hello"); </script> <xf:message level="modal" id="hndl_id">goodbye</xf:message> </head> <body id="bod_id" ev:event="unload" ev:handler="#hndl_id"> </body> </html>
|
这里的
script
元素是一个处理程序,被附加到
bod_id
上的一个观测者(
body
元素)。
xf:message
元素本身什么也不做,但观测者元素(还是
body
)又指向了它,注册了事件处理程序。
结束语
脚
本显然在很多方面都很有用。尽管如此,它仍然难以通过脚本完成某些任务。XML Events
为通过声明将脚本附加到文档中提供了统一的语法,甚至能够定义不依赖于脚本解释器的行为。现在有越来越多的标记技术开始采用 XML
Events,其中包括 XForms、XHTML、SVG 等。在以后的文章中,我将详细探讨如何使用和实现 XML Events。
参考资料
关于作者