事件和动作
XForms 采用 XML Events 标准为特定文档元素增加事件处理程序。XML Events 是 HTML DOM 事件模型的 XML 表示。我们来简单地了解一下事件模型,此前您可能没有遇到过。
事件是如何分派的
当一个事件发生时(比如单击或者鼠标划过),捕获阶段就开始了。从文档树的根开始向下移动到触发事件的元素,其中每个元素都有机会处理该事件。如果事件抵达目标元素而没有被处理并且该事件类型允许,则进入冒泡阶段。事件往回走直到文档根。
元素可以是观察者(observer),当事件在两个方向上经过时,观察者的事件处理程序就会被激活。事件处理程序可以只监听一个阶段,如果需要在两个阶段做不同的工作就需要为一个元素附加上两个处理程序。
清单 1 显示了一个非常简单的 XHTML 文档。在 Web 浏览器中打开该文档时,就会创建如图 1 所示的 DOM 树来表示该文档。
清单 1. 简单的 XHTML 文档
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Event path</title> </head> <body> <h1>Event path</h1> <p> What happens if you click on <em>this</em> element? </p> </body> </html>
如果用户单击 <p>
元素中突出显示的 "this",一个事件就会沿着图 1 中红色的路线移动,这代表捕获阶段。如果没有事件处理程序处理这个单击事件,它就会沿着同样的路线再返回到根元素,这时根元素默认的单击处理程序将忽略该事件。
图 1. 事件在 DOM 树中的移动
在 HTML 中,可以为不同的事件附上 JavaScript 动作来处理它,比如 onclick
或 mouseover
。例如,如果将清单 1 中 <p>
元素的代码改为清单 2 中的形式,单击 "this" 的时候将为用户显示一个 JavaScript™ 警告框。这是一个单击事件,由一个观察者(<em>
元素)接收,一个处理程序(JavaScript 片段)处理。
清单 2. XHTML 事件处理程序
<p> What happens if you click on <em οnclick="alert('Click!'); return true;">this</em> element? </p>
这种技术已经用了多年,为什么对 XForms 来说还不够好呢?事件名称是硬编码到语言中的(这里是 XHTML);增加新的事件必须增加新属性。而且事件名称非常特定于硬件,比如当元素被激活时的“onclick”事件(可能是鼠标单击,也可能是通过按下键盘上的回车键选中并激活,或者能够想象到的其他任何方式)。创建一个事件处理程序只能使用一种脚本语言,因为观察者的一个属性不能有多个实例。而且,事件处理脚本和表单的表示标记混杂在一起。
XForms 通过 XML 并利用 XML Events 标准(请参阅参考资料)解决了这些问题。事件、观察者和处理程序作为 XForms 控件的一部分指定。比如,清单 3 中的 XHTML 在单击时将弹出警告框。
清单 3. 内嵌在 <button>
元素中的事件和处理程序
<button name="Click me" οnclick="alert("You clicked me!"); return true;"/>
清单 4 说明了如何使用 XForms 达到同样的目标。
清单 4. XForms 事件和处理程序
<xf:trigger> <xf:label>Click me</xf:label> <xf:message level="modal" ev:event="DOMActivate">You clicked me!</xf:message> </xf:trigger>
这样就创建了一个观察者,它等待 DOMActivate
事件(和 XHTML 的 onclick
事件相同)来执行触发器的 <xf:message>
动作(显示一条警告消息)。单击触发器的时候,它就会弹出一条警告。
XForms 标准列出了很多事件,可以以控件(<xf:group>
、<xf:input>
、<xf:output>
、<xf:range>
、<xf:submit>
、<xf:secret>
、<xf:select>
、<xf:select1>
、<xf:textarea>
、<xf:trigger>
或 <xf:upload>
元素中的任何一个)、模型、<xf:submission>
和实例为目标。关于各种事件及其用法的参考信息请查看参考资料部分。
如清单 4 所示,XForms 事件处理程序(如 <xf:message>
)被捆绑到特定的事件上。事件处理程序中有一些 XForms 动作可以使用:
-
<xf:action>
——其他动作的容器,这些动作将按照在<xf:action>
元素中指定的顺序调用。 -
<xf:dispatch>
——向给定的目标元素分派事件(无论是自己建立的还是预定义的 XForms 事件)。可以指定在目标元素不处理的情况下是否让事件冒泡返回到根。 -
<xf:load>
——加载指定的 URL,可以在新窗口中打开或者替换当前文档。 -
<xf:message>
——向用户显示指定的消息(可存在于实例中、从外部文件中载入或者封装在<xf:message>
元素中)。 -
<xf:rebuild>
——使 XForms 处理程序重建任何内部数据结构来跟踪实例数据元素之间的依赖关系。引发xforms-rebuild 事件
。 -
<xf:recalculate>
——重新计算和更新实例数据元素。引发xforms-recalculate
事件。 -
<xf:refresh>
——刷新 XForms 用户界面,更新控件以反映实例的当前状态。引发xforms-refresh
事件。 -
<xf:reset>
——通过分派xforms-reset
事件重置表单。可能永远不会用到这个动作,因为用户不可能把表单恢复到原来的状态。 -
<xf:revalidate>
——重新验证处理模型指定的实例数据。引发xforms-revalidate
事件。 -
<xf:send>
——分派xforms-submit
事件,启动表单提交处理。 -
<xf:setfocus>
——分派xforms-focus
事件给指定的控件。可用于实现可访问性等特性。 -
<xf:setvalue>
——显式地设置指定数据实例元素的值。
下面我们以本系列文章第一部分创建的简单搜索表单为例。您可能还记得,表单中只有一个搜索关键字文本的输入字段和提交搜索的按钮。清单 5 中增加了一些 DOMFocusIn
事件处理程序(突出显示部分),为控件添加了“ephermeral”提示效果。
清单 5. 用 <xf:message>
添加帮助消息
<?xml version="1.0" encoding="UTF-8"?> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:xf="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ev="http://www.w3.org/2001/xml-events"> <head> <title>Search Form</title> <xf:model> <xf:instance> <data xmlns=""> <query/> </data> </xf:instance> <xf:submission action="http://localhost/imaginary-search-engine" method="get" id="submit-search"/> </xf:model> </head> <body> <h1>Search Form</h1> <p> Enter a search string, then click the Search button. </p> <p> <xf:input ref="query"> <xf:label>Find:</xf:label> <xf:message level="ephemeral" ev:event="DOMFocusIn"> Enter your search keywords here, then click Search. </xf:message> </xf:input> <xf:submit submission="submit-search"> <xf:label>Search</xf:label> <xf:message level="ephemeral" ev:event="DOMFocusIn"> Click here to begin searching for your keywords. </xf:message> </xf:submit> </p> </body> </html>
这些内容常常呈现为工具提示或者类似的小窗口,如图 2 所示。
图 2. <xf:message>s
的效果
现在您已经很好地了解了如何在 XForms 中处理事件,下面讨论一些更高级的提交主题和选项。
提交格式和选项
本系列的第 2 部分简要介绍了各种基本 XForms 提交方法,这里总结一下:
<xf:submission method="form-data-post" action="url"/>
和 HTML 中的 <form method="post" enctype="multipart/form-data">
相同,可用同样的方式在接收 URL 中处理。数据实例被序列化为包含多部分的表单数据。
<xf:submission method="get" action="url"/>
和 HTML 中的 <form method="get">
相同。数据实例用 URL 编码序列化并追加到指定 URL 之后。
<xf:submission method="post" action="url">
将表单的当前数据模型实例作为 XML 文档发送到指定 URL。HTML 中没有等价的形式。
<xf:submission method="put" action="url">
将表单的当前数据模型实例作为 XML 文件写入指定 URL,需要服务器允许该 URL 使用 PUT
方法。HTML 中没有等价的形式。
<xf:submission method="urlencoded-post" action="url">
和 HTML 中的 <form method="post" enctype="application/x-www-form-urlencoded">
相同。数据实例序列化为多部分的相关数据。
put
and get
方法支持 file: 和 http:/https: URL,其他方法支持 http:/https: 和 mailto: URL。
<xf:submission>
元素支持几种可选的属性来帮助控制 XForms 处理程序从数据模型实例生成的 XML:
-
version
——指定创建的 XML 版本(显然,默认为“1.0”) -
indent
——指定生成的 XML 是否包含空白来提高可读性 -
mediatype
——指定生成 XML 的 MIME 类型(要保证媒体类型和默认的 application/xml 兼容) -
encoding
——指定生成 XML 的文本编码 -
omit-xml-declaration
——指定是否要省略 XML 声明 -
standalone
——指定是否在 XML 中包含独立性声明 -
cdata-section-elements
—— 以逗号分隔的元素列表,这些元素应被序列化为 CDATA 节 -
includenamespaceprefixes
——逗号分隔的名称空间列表,输出 XML 中包括与这些名称空间匹配的元素。
separator
属性也可用于指定 URL 键值对的分隔字符(默认为“;”)。
稍微修改一下基本搜索表单以使用 put
输出到文件,这样就能看到清单 6 所示的 XML 输出结果,它说明了 XForms 从数据模型实例创建的默认 XML 是什么样子的。其中包括 XForms 文档中的 XML 名称空间声明,不一定要适合该实例。
清单 6. 示例 XForms XML 输出
<?xml version="1.0" encoding="UTF-8"?> <data xmlns:xf="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ev="http://www.w3.org/2001/xml-events"> <query>xforms standard</query> </data>
为 <xf:submission>
元素增加 includenamespaceprefixes="#default"
将得到清单 7 所示的文档,它只包括分配给模型的默认名称空间。该例中没有其他名称空间,因此所得到的这个简单无修饰的 XML 文档只包含数据模型实例中的数据。
清单 7. 只有默认名称空间
<?xml version="1.0" encoding="UTF-8"?> <data xmlns=""> <query>another test</query> </data>
使用 <xf:submission>
及其选项可以控制数据如何发送到服务器上。
没有目标的表单
使用 XForms,建立不带任何 <xf:submission>
元素的表单是完全可能的。数据随着用户 Web 浏览器打开和关闭而建立和消失,但是仍然有某些用处。比方说,假设您是一位学习计算机的高中生(如图 3 所示),编写了一个温度换算程序。
图 3. 高中生的计算机作业:编写一个温度换算程序
如果您的网友抱怨他们那儿有多冷或者多热,您可能希望能够很快确定气温到底多高,那么这是一个很方便的工具(如清单 8 所示)。
清单 8.XForms 风格的温度换算程序
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/ TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:xf="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ev="http://www.w3.org/2001/xml-events"> <head> <title>Temperature Converter</title> <xf:model> <xf:instance> <data xmlns=""> <f/> <c>20</c> </data> </xf:instance> </xf:model> </head> <body> <h1>Temperature Converter</h1> <p> Enter a value, then click the appropriate button to convert between units: </p> <p> <xf:input ref="c"><xf:label>Degrees Celcius (°C): </xf:label></xf:input> </p> <p> <xf:input ref="f"><xf:label>Degrees Fahrenheit (°F): </xf:label></xf:input> </p> <p> <xf:trigger> <xf:label>°C → °F</xf:label> <xf:action ev:event="DOMActivate"> <xf:setvalue ref="f" value="number(/data/c) * 9 div 5 + 32"/> </xf:action> </xf:trigger> <xf:trigger> <xf:label>°F → °C</xf:label> <xf:action ev:event="DOMActivate"> <xf:setvalue ref="c" value="( number(/data/f) - 32 ) * 5 div 9"/> </xf:action> </xf:trigger> </p> </body> </html>
清单 8 中的 XForms 代码已经被突出显示。这里首先声明了一个非常简单的数据模型,它知道气温的摄氏度(公制)和华氏度(英制)。只需要填入一个数据,毕竟,我们希望计算机来完成这个转换而不是手工计算。
另外,还创建了两个输入字段,让用户可以用任何一种单位输入温度值。要注意,这里没有使用 <xf:bind>
元素限制输入数据必须是一个数字,这是本系列没有涉及到的 XForms 高级主题之一。
最后,一个按钮将摄氏度换算成华氏度,另一个按钮将华氏度换算成摄氏度。在 <xf:action>
元素中,使用 <xf:setvalue>
来计算另一种温度值而没有使用任何脚本(仅仅是 value
属性中的 XPath 表达式),也不需要服务器的任何干预。
这个温度换算程序完全体现了 XForms 的目标,在不增加 Web 服务器负担的情况下提供更丰富的客户体验。
结束语
本文简要介绍了各种 XForms 动作,说明了如何在 XForms 控件中为 Xforms 事件创建观察者。此外还讨论了各种提交技术以及用于控制发送到服务器上的数据的选项。
本系列文章已经为您提供了建立 XForms 表单的全部基础知识。现在您就可以自己体验一下 XForms 了!