2008 年 2 月 13 日
在 HTML 表单中结合 Asynchronous JavaScript™ + XML (Ajax) 的服务器回调机制,对表单应用 Ajax,对于为应用程序增加 Web 2.0 功能来说是一种可行的办法。通过本文了解增加 Ajax 代码改进 PHP 应用程序用户体验的各种技术。
谈到 Web 2.0 应用程序,首先想到的就是那些最杰出的应用:YouTube 的视频、 üGoogle Maps 可滚动的地图以及 Flikr 的地理定位功能。但是人们常常忽略的是,在这些网站上那些粗糙的 HTML 表格正随着 Ajax 技术的普及经历一场重大的变革。
|
本文介绍了如何利用 Prototype.js JavaScript 库在表单中结合 Ajax 代码来解决常见的用户体验问题。
我们从最简单的东西入手:一个包含多个字段的注册表单,希望通过 Ajax 而不是常规的 Web 表单上传办法来提交。清单 1 显示了这个简单的表单。
清单 1. index.html
<html> <head> <script src="prototype.js"></script> </head> <body> <form id="myform"> <table> <tr><td>First</td><td><input type="text" name="first"></td></tr> <tr><td>Last</td><td><input type="text" name="last"></td></tr> <tr><td>Email</td><td><input type="text" name="email"></td></tr> </table> <input type="button" οnclick="dosubmit()" value="Submit"> </form> <div id="result" style="padding:5px;"> </div> <script> function dosubmit( ) { new Ajax.Updater( 'result', 'add.php', { method: 'post', parameters: $('myform').serialize() } ); $('myform').reset(); } </script> </body> </html> |
文件的一开始包含了 prototype.js JavaScript 文件,它将帮助我完成所有的 Ajax 操作。接下来是传统的 HTML 表单,包含三个字段:first、last 和email。再下面是使用 dosubmit()
JavaScript 函数提交表单的按钮。
dosubmit()
函数使用 Ajax.Updater
类把数据上传给 add.php 脚本。然后添加调用选项。这里将提交方法设置为 post
,然后使用表单的 serialize()
方法添加参数。serialize()
方法不是标准的 JavaScript 代码,由 JavaScript 库来提供。
调用 Ajax.Updater
的第一项是 <div>
标记的 ID,它接收 add.php 脚本返回的 HTML。在用户单击按钮时通知用户发生了什么,这是最简单的办法。
add.php 脚本如清单 2 所示。
清单 2. add.php
Thanks <?php echo( $_POST['first'] ) ?> <?php echo( $_POST['last'] ) ?>! |
脚本仅仅回显表单上传的内容。事实上,这时候可能需要增加一条数据库记录,这类业务逻辑处理您可以自己完成。
第一次打开表单的时候,浏览器中显示的结果如 图 1 所示。
图 1. 简单的 Ajax 表单
然后单击 Submit,将表单数据发送到页面,add.php 页面返回的 HTML 显示在 Submit 按钮下面,如 图 2 所示。
图 2. 提交之后的响应
Ajax 表单的另一个变化是自动填充表单,根据某些关键字段的值更新字段值 — 比如,输入客户 ID 并单击按钮就可以在另一个字段中得到当前客户的记录。
这类自动填充表单如 清单 3 所示。
清单 3. index.html
<html> <head> <script src="prototype.js"></script> </head> <body> <form id="myform"> <table> <tr><td>ID</td><td><input type="text" name="id"></td></tr> <tr><td>First</td><td><input type="text" name="first" id="elFirst"></td></tr> <tr><td>Last</td><td><input type="text" name="last" id="elLast"></td></tr> <tr><td>Email</td><td><input type="text" name="email" id="elEmail"></td></tr> </table> <input type="button" οnclick="dofill()" value="Fill Fields"> </form> <script> function dofill( ) { new Ajax.Updater( 'result', 'getdata.php', { method: 'post', parameters: $('myform').serialize(), onSuccess: function( transport ) { $('elFirst').value = transport.responseXML.getElementsByTagName('first') [0].firstChild.nodeValue; $('elLast').value = transport.responseXML.getElementsByTagName('last') [0].firstChild.nodeValue; $('elEmail').value = transport.responseXML.getElementsByTagName('email') [0].firstChild.nodeValue; } } ); } </script> </body> </html> |
在 清单 1 所示表单的基础上增加了一个字段 ID。这里存放客户的 ID。然后新增的 dofill()
函数调用 getdata.php 页面,后者返回包含给定客户名字、姓氏和电子邮件地址的 XML 代码。
发送给 Ajax.Updater
调用的 onSuccess
处理程序使用所有浏览器都支持的文档对象模型(DOM)函数解析下载的 XML 数据。然后将 elFirst
、elLast
和elEmail <input>
项设置为 XML 返回的数据。
getdata.php 页面如 清单 4 所示。
清单 4. getdata.php
<?php header( "content-type: text/xml" ); $first = ' '; $last = ' '; $email = ' '; if ( $_POST['id'] == '1' ) { $first = 'Jack'; $last = 'Herrington'; $email = 'jherr@pobox.com'; } ?> <data> <first><?php echo( $first ) ?></first> <last><?php echo( $last ) ?></last> <email><?php echo( $email ) ?></email> </data> |
这里的代码实际上是一个存根方法,通常需要访问数据库来得到给定记录的名字、姓氏和电子邮件地址。
第一次打开该页面的结果如 图 3 所示。
图 3. 填充表单
然后在 ID 字段中输入 1
并单击 Fill Fields,于是访问 getdata.php 页面获得名字、姓氏和电子邮件地址并更新相应的字段。如 图 4 所示。
图 4. 根据 ID 填充字段
下一个 Ajax 小例子将创建一个就地更新(in-place)的 to-do 列表。
|
最常见的 Ruby on Rails 范例是原地 更新的 to-do 列表。就是说列表在页面上部,列表下有一个文本框。在文本框中输入一些内容并单击 Submit 之后,页面上方的列表就会加上新的项目而不改变页面。输入文本的文本框重置以便输入新的内容。
实际上非常困难,因此采用 PHP 来完成。to-do 列表所在页面如 清单 5 所示。
清单 5. index.php
<html> <head> <script src="prototype.js"></script> </head> <body> <div id="result" style="padding:5px;"> <?php $fh = fopen( 'list.txt', 'r' ); while( $str = fgets( $fh ) ) { ?> <?php echo( $str ); ?><br/> <?php } ?> </div> <form id="myform"> <input type="text" name="todo"> </form> <input type="button" οnclick="dosubmit()" value="Submit"> <script> function dosubmit( ) { new Ajax.Updater( 'result', 'add.php', { method: 'post', parameters: $('myform').serialize() } ); $('myform').reset(); } </script> </body> </html> |
这里没有将 to-do 列表存储到数据库中,而采用普通文本文件 list.txt 存储,每行一项。因此,要将列表置于页面顶部,只需要打开文件并把每一行读入 ID 为 result
的 <div>
标记中。
表单如下,其中包含作为 to-do 项的输入文本。其 下方是调用 dosubmit()
JavaScript 函数的按钮。这个 JavaScript 函数使用 Ajax.Updater
类调用 add.php 页面,后者添加新的项并返回添加了新项的列表。
add.php 脚本如 清单 6 所示。
清单 6. add.php
<?php $total = ''; $fh = fopen( 'list.txt', 'r' ); while( $str = fgets( $fh ) ) { ?> <?php echo( $str ); ?><br/> <?php $total .= $str; } if ( array_key_exists( 'todo', $_POST ) ) { ?> <?php echo( $_POST['todo'] ); ?><br/> <?php $fh = fopen( 'list.txt', 'w' ); fwrite( $fh, $total."/n".$_POST['todo'] ); fclose( $fh ); } ?> |
有点傻里傻气的 to-do 列表如 清单 7 所示。
清单 7. list.txt
Get swim goggles Practice swimming Swim in race |
第一次打开页面的结果如 图 5 所示。
图 5. 准备添加 to-do 项
输入 Finish in record time
然后单击 Submit。在页面没有刷新的情况下得到的结果如 图 6 所示。
图 6. 插入记录后的页面
当然这绝不是 Rails to-do 列表范例惟一的精彩之处。但的确是 Web 2.0 令人侧目的因素之一。实际上,如果还没有尝试过 Rails,我建议所有的 Web 工程师都应当试一试。即便不在项目中应用,看看应用程序是如何组织的、MVC 机制是如何使用的、数据库持久模型是多么简单也很有意思。
另一种常见的 Web 需求是扩展字段列表。我称之为 expando 列表。
|
如果某条记录有无数多个关键字怎么办?当然,一种办法是使用逗号分隔关键字。另一种办法是让按钮随时增加新的关键字,允许用户根据需要添加任意多的关键字。清单 8 采用了第二种办法。
清单 8. index.html
<html> <head> <script src="prototype.js"></script> </head> <body> <form id="myform"> <table id="keytable"> <tr><td>Keyword</td><td><input type="text" name="keyword_1"></td></tr> </table> </form> <input type="button" οnclick="addkeyword()" value="Add Keyword"> <input type="button" οnclick="dosubmit()" value="Submit"> <div id="result" style="padding:5px;"> </div> <script> var nextkeyid = 2; function addkeyword() { var elTR = $('keytable').insertRow( -1 ); var elTitleTD = elTR.insertCell( -1 ); elTitleTD.appendChild( document.createTextNode( 'Keyword' ) ); var elInput = document.createElement( 'input' ); elInput.type = 'text'; elInput.name = 'keyword_'+nextkeyid; nextkeyid++; var elInputTD = elTR.insertCell( -1 ); elInputTD.appendChild( elInput ); } function dosubmit( ) { new Ajax.Updater( 'result', 'add.php', { method: 'post', parameters: $('myform').serialize() } ); } </script> </body> </html> |
其中的关键在于 addkeyword()
函数,它使用 insertRow
和 insertCell
在关键字表格中创建新行。然后用 document.createElement
建立存放关键字的新的输入字段。Ajax.Updater
在用户单击 Submit 时被调用,本身则调用 add.php 脚本。add.php 脚本返回表单给出的关键字列表。脚本如 清单 9 所示。
清单 9. add.php
Post Result:<br/> <?php var_export( $_POST ) ?> |
在浏览器中打开的时候,页面如 图 7 所示。
图 7. 只有一个关键字的表单
多次单击 Add Keyword 添加新的字段,然后单击 Submit。结果如 图 8 所示。
图 8. 添加关键字并提交之后的表单
为了让用户添加和同一记录关联的多个值,比如电话号码、关键字和地址,这是一种理想的办法。Ajax 另一种常见的用法是实现登录表单。
|
Ajax 登录窗口非常棒,因为能够马上得到反馈是否能够登录进去,而且可完成原地登录。假设您正在寻找一篇希望提供建议的文章,但是没有登录。利用 Ajax 可以在查找的同时登录。如果身份得到认可,将出现一个新表单供您添加评论。这种方法和跟踪查找的文章然后在登录之后重定向相比要容易得多。
Ajax 登录的简单版本如 清单 10 所示。
清单 10. index.html
<html> <head> <script src="prototype.js"></script> </head> <body> <form id="logform"> User: <input type="text" name="user"><br/> Password: <input type="password" name="password"><br/> <input type="button" οnclick="login()" value="Login"> </form> <div id="noway" style="display:none;"> No way! </div> <script> function login() { new Ajax.Request( 'login.php', { method: 'post', postBody: $('logform').serialize(), onSuccess: function( transport ) { if( transport.responseText.match( //<ok///>/ ) ) window.location = 'home.html'; else $('noway').style.display='block'; } } ); } </script> </body> </html> |
User 和 Password 字段位于文件上方的表单。此后是 ID 为 noway
的 <div>
标记,登录不成功的话将显示它。login()
JavaScript 方法使用Ajax.Request
类尝试登录。若 login.php 返回的 XML 是 <ok />
,表单把用户重定向到主页。否则显示 noway
文本。
login.php 代码如 清单 11 所示。
清单 11. login.php
<?php header( 'Content-type: text/xml' ); if ( $_POST['user'] == 'jack' && $_POST['password'] == 'password' ) echo( "<ok/>" ); else echo( "<bad/>" ); ?> |
这段简单的代码检查硬编码的用户和密码,匹配返回 ok
,否则返回 bad
。
为了完整起见,主页代码如 清单 12 所示。
清单 12. home.html
<html> <body> You are logged in and this is your home page. </body> </html> |
在浏览器中打开该页然后输入错误的用户名和密码并单击 Login。结果如 图 9 所示。
图 9. 输入错误密码后的登录页面
然后修改用户名和密码并单击 Login,就会重定向到主页,如 图 10 所示。
图 10. 输入正确用户名和密码之后的登录页面
本文的最后一个例子是基于 XForms 的 Ajax 文章。
|
我不认为自己是一位 XForms 专家,但认为这是一个不错的标准。本文将客户端 XForms、Protoype.js 与 Ajax 技术结合了起来。
这个简单的 XForms 例子如 清单 13 所示。
清单 13. index.xhtml
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xforms="http://www.w3.org/2002/xforms"> <head> <title>XForms AJAX Example</title> <xforms:model id="modelData"> <xforms:instance xmlns=""> <Data> <First>First</First> <Last>Last</Last> <Email>email@email.com</Email> </Data> </xforms:instance> </xforms:model> <script src="prototype.js"></script> </head> <body> <xforms:input ref="/Data/First"> <xforms:label>First: </xforms:label> </xforms:input><br/> <xforms:input ref="/Data/Last"> <xforms:label>Last: </xforms:label> </xforms:input><br/> <xforms:input ref="/Data/Email"> <xforms:label>Email: </xforms:label> </xforms:input><br/><br/> <button οnclick="submit()">Submit</button> <script> function submit() { var m = $('modelData'); var base = m.getElementsByTagName('Data')[0]; var s = new XMLSerializer(); var data = ( s.serializeToString( base ) ).toString(); new Ajax.Updater( 'result', 'params.php', { method: 'post', parameters: 'data='+escape( data ) } ); } </script> <br/><br/> <div id="result"> </div> </body></html> |
文件开始部分是 XForm 的 XML 模型。接下来是一些 XForms 标签和组成表单的输入字段。Submit 按钮调用 submit()
JavaScript 函数。该函数利用XMLSerializer
JavaScript 对象将 XForms 数据模型转化成 XML 字符串传递给 params.php。params.php 脚本如 清单 14 所示,它把 XML 作为 HTML 返回以便能看到页面输出的内容。
清单 14. params.php
<?php echo( htmlentities( $_POST['data'] ) ); ?> |
在浏览器中(安装了 XForms 增件)打开 XForms 页面的结果如 图 11 所示。
图 11. 支持 Ajax 的 XForms 页面
然后填充表单并单击 Submit 得到 图 12 所示的结果。
图 12. 提交之后的页面
它显示了转化成 XML 文本、发送给 params.php 然后返回到 result <div>
标记的数据模型。
|
利用 Ajax 来改进 HTML 表单有很多事情可做,本文仅仅介绍了一点皮毛。但至少希望能帮助您了解如何稍加修改页面代码来改进您的应用程序。
|
描述 | 名字 | 大小 | 下载方法 |
---|---|---|---|
表单应用程序的源代码 | x-ajaxxml9-forms.zip | 101KB | HTTP |
关于下载方法的信息 |
学习
- 您可以参阅本文在 develperWorks 全球网站上的 英文原文。
- Ajax 技术资源中心:developerWorks 上所有有关 Ajax 的问题都可以在这里找到解答。
- 订阅 Ajax 相关文章和教程 的 RSS 提要:获得即将发表的 Ajax 相关文章和教程的通知(查看 developerWorks 内容 RSS 提要 了解更多的信息)。
- PHP 主页:为 PHP 程序员提供了宝贵的资源。
- Prototype 库:了解这种用于简化动态 Web 应用程序开发的 JavaScript Framework。
- Scriptaculous JavaScript 库:这个基于 Prototype 的框架提供了改善网站体验的显示帮助器和特效。
- XForms Firefox 增件:安装后才能运行本文中的 XForms 例子。
- Prototype.js 文档:进一步了解 Prototype JavaScript 库,提供了官方 Prototype blog 和其他很多资源的链接。
- jQuery:另一个 JavaScript 库,提供了类似 Prototype.js 的功能。
- Yahoo! User Interface Library:看看 Yahoo! 的 Ajax 工具箱。
- developerWorks XML 专区:通过 developerWorks XML 专区了解 XML。
- IBM XML 认证:看看如何才能成为一名 IBM 认证的 XML 及相关技术的开发人员。
- XML 技术文档库:developerWorks XML 专区提供了大量技术文章和技巧、教程、标准以及 IBM 红皮书。
- developerWorks 技术事件和网络广播:随时关注技术的最新进展。
讨论
- 参与论坛讨论。
- XML 专区讨论论坛:参与任何面向 XML 的论坛。
- developerWorks XML 专区论坛:分享您的观点:阅读本文后在这个论坛上提交您的见解和建议。XML 专区编辑负责该论坛,欢迎您的参加。
Jack D. Herrington 是一位有着超过 20 年经验的高级软件工程师。他是 Code Generation in Action、Podcasting Hacks 和 PHP Hacks 这三本书的作者。他还发表了 30 多篇文章。可以通过 jherr@pobox.com 与 Jack 联系。 |