<script language="JavaScript" type="text/javascript">
</script>
级别: 初级
Brian J. Dillard (bdillard@pathf.com ), RIA 推广者, Pathfinder Development
2008 年 5 月 22 日
本文是使用 Ajax 改造现有 Web 站点系列的第一篇文章,将展示如何使用简单的模式窗口(modal window)消除弹出窗口和导航死角。
Ajax 技术改变了大型商业 Web 应用程序的外观,但是许多较小的 Web 站点都不具备足够的资源重新构建完整的用户界面(UI)。Ajax 的一些新特性能够解决实际中的界面问题并改善用户体验。本文将展示如何使用简单的模式窗口消除弹出窗口和导航死角。通过应用渐进增强(progressive enhancement) 这一理念,能够保证这些增强的 UI 特性不会损害站点的可访问性,并且严格遵守 Web 标准。
developerWorks Ajax 资源中心 请访问 Ajax 资源中心 ,这是与开发 Ajax 应用程序相关的免费工具、代码和信息的一站式中心。活跃的 Ajax 社区论坛 由 Ajax 专家 Jack Herrington 主持,这里的同行或许可以帮助您解答疑问。
本文假设您已经牢固掌握超文本标记语言(Hypertext Markup Language,HTML)和级联样式表(Cascading Style Sheet,CSS),基本了解 JavaScript 编程和 Ajax。示例应用程序仅使用客户端代码构建;本文演示的技术适用于任何服务器端应用程序框架。要运行示例站点,您至少需要在本地主机上运行一个基本的 Web 服务器。此外,您也可以仅跟随源代码并在我的 Web 服务器上查看运行中的示例站点(参见 参考资料 中的链接)。
概念介绍:使用 Ajax 改进您的站点
引导用户跟随特定路径 — 即,从产品搜索到付款购买 — 这种需求与 Web 本身的历史一样久。它一直都充满着风险:让用户迷失方向。您的导航路径越长、越复杂,用户经历的内容就越多。您需要为用户提供足够的信息,以防止他们在导航过程中失去兴趣。
本文的任务 本文将逐步引导您使用 Ajax 技术改进 Web 购物站点。改进 “之前” 和 “之后” 的示例应用程序源代码在 下载 一节可以找到。您也可以在我的 Web 服务器上查看运行中的两个版本(参见 参考资料 )。除了 Ajax 技术和最佳实践之外,您还将了解如何遵循渐进增强原理,使用 Ajax 改善用户体验。
Wikipedia 将渐进增强 定义为一种应用于 Web 设计的策略,它强调可访问性、语义标记、外部样式表和脚本编制技术。渐进增强通过分层的方式使用 Web 技术,允许所有人使用任何浏览器或 Internet 连接访问 Web 页面的基本内容和功能,同时还提供更好的带宽或更先进的浏览器软件,实现增强的页面。
在 Web 1.0 世界,购物站点通过构造一条流畅的路径(从搜索和结果到选择和购买)流线化用户体验。当购买路径提供的信息无法满足用户需求时,他们需要通过导航找到提供更多信息的产品细节或比较页面。这种方法存在的问题是,它们使用户离开了购买路径,增加了用户放弃购买的机会。而且还难以维护,因为您的导航逻辑必须存储与用户如何到达此死角相关的信息。
弹出窗口似乎提供了一个解决方案。通过弹出窗口提供补充信息,主窗口中直接的分布路径不会受到干扰。然而,不幸的是,弹出窗口容易使人混淆和反感。它们可能比上面的导航弯路更容易维护,但是它们很可能使用户退出您希望其完成的购买流程。
幸运的是,开源 JavaScript 库提供了一种简单方式,可以彻底摆脱导航弯路和弹出窗口。本文将演示如何使用 Ajax 和 Dynamic HTML (DHTML) 技术在工具提示、lightbox 和其他模式窗口中呈现补充信息。由于这些元素可以动态地插入任何页面,它们能够保持从主页到购买的快速、分步路径。
应用程序介绍:Customize Me Now
本文中的示例应用程序主要针对电子商务。我已经构造了一个虚构的购物应用程序 Customize Me Now ,允许用户定制和购买一组不同的产品:批萨、旅行包或有价证券。当然,在实际中,这些产品类别绝不会出现在同一个站点上。但是,将它们放在一起可以演示许多站点面对的复杂、真实的导航难题。
本文首先提供一个 Web 1.0 版的 Customize Me Now,然后将其改进为 Web 2.0 版,当您了解到补充信息只是通过一个 Ajax 调用提供时,就能想象到导航路径有多么的流畅。此处涉及的技术适合于任何需要保持简单性并能引导用户的流程。配置产品、做一个调查、注册一些服务,或者只是完成一个注册表单 — 所有这些流程都可以使用 Ajax 实现流线化。
技术介绍:Ajax、工具提示、模式窗口和 lightbox
现在,也许不再需要介绍 Ajax 了:在 Web 开发领域到处都是它身影。这些年来,聪明的编程人员一直使用 JavaScript 代码逐步更新 Web 页面,而不再与服务器往返通信。但仅仅是因为 xmlHttpRequest
对象的采用 — 最初是一个 Windows® Internet Explorer® 扩展,但现在支持许多不同的浏览器 — 才使 Ajax 流行起来的。无论何时,只要您看到一个与桌面应用程序非常相似的 Web 应用程序,那么它很可能采用了 Ajax。本文无意讨论 Ajax 编程的基础知识,但是您将使用到许多使用 Ajax 技术的开源库。
Ajax UI 通常使用工具提示、lightbox 和模式窗口。它们都是指一个浏览器视区(viewport)的弹出屏幕,而不是在一个独立的窗口中运行。工具提示(Tooltips) 通常是一些提供上下文内容的小型窗口,将鼠标悬停在一个触发器元素上就会显示。模式窗口(Modal windows) 通常比较大,而且通过一个单击事件触发。Lightbox 是一种特殊的模式窗口,它通过半透明的重叠将窗口的原始内容和模式内容分隔开。任何一种这类容器都可以填充各种内容:使用 DHTML 技术隐藏的内联内容;通过 Ajax 调用从服务器拉取的新内容;或者拉入 iframe 中的完整的独立文档。流行的 DVD 租赁服务 Netflix 提供了一个关于这些界面元素的出色示例。
工具介绍:jQuery、GreyBox、ThickBox、JTip 和 jQuery 表单
自从 2005 年 Ajax 开始流行之后,开源 JavaScript 工具箱不断增多。每个工具箱都有自己的优点、缺陷和特性,但是出色的工具箱隐藏了浏览器的差异,并提供了一个针对 Ajax、DHTML 和视觉效果的出色的跨浏览器应用程序编程接口(API)。一些解决方案(比如 Google Web Toolkit)使用服务器端 Java™ 代码自动生成客户端 JavaScript 代码。但是,大多数都使用本机 JavaScript 库。
您将使用一个这样的库,jQuery。自从 2006 年首次登场,jQuery 因其优雅的 API 和谦逊的 JavaScript 而变得非常流行。使用 jQuery,几乎无需对服务器代码、HTML 标记或 CSS 进行任何更改,您就可以将一个未使用 Ajax 的 Web 1.0 站点转变为一个通过 Ajax 增强的 Web 2.0 站点。在运行时,您的 JavaScript 代码将会在浏览器内部转换现有的 HTML 元素和行为。如果 JavaScript 被禁用或者不受浏览器支持,标记将会按原来的方式运行。这种渐进增强 原理可以确保您的 Web 应用程序仍然能够被广大用户访问。物理条件较差的移动设备、辅助性软件,或者甚至是 10 年前的 Web 浏览器:所有这些都能够运行您的应用程序。
除了支持渐进增强之外,jQuery 还允许您利用大型的、活跃的插件开发人员社区。许多 JavaScript 工具箱都试图预测所有可能的开发人员需求,但是 jQuery 只关注基本原理。其核心库仍然很简洁,而附加功能是通过开源社区的插件来提供的。具体来讲,您可以利用以下插件:
ThickBox: 由 Cody Lindley 开发 jQuery Forms: 这是由 jQuery 社区合作开发 JTip: 由 Cody Lindley 开发,并由 jQuery 社区进行扩展 GreyBox: 由 Amir Salihefendic 开发 了解应用程序:Customize Me Now 1.0
以下小结帮助您了解 Customize Me Now 1.0 — 它的用户体验和源代码。
用户体验
图 1 展示了 Customize Me Now Results 页面和一个信息弹出窗口。它演示了站点能够向用户提供的与他们定制的产品有关的信息。尽管最佳的用户路径是从搜索和结果到定制和购买的直线路径,但该界面却提供了一些迂回的路径。站点使用传统的弹出窗口显示每个产品和产品选项的一小段上下文信息。单击 Pizza ,您将会看到关于站点的批萨产品的信息。单击 Cheese ,您将会看到关于您的批萨可用的奶酪的信息。此外,用户可以导航到厂商的 Web 站点 — 同样通过弹出窗口呈现。
图 1. Results 页面 1.0 如果用户想要获得更多信息,站点就会提供一个 Product Details 页面,其中包括每个产品的更详细信息 —— 图片、文章、用户评论等等。最后,如果用户想要将一个产品与其他产品进行比较,他可以在 Comparison 页上逐个查看这些产品。在这些资源中进行导航非常复杂。由于用户需要定制每个产品,很难在每步操作中都提供一个 Add to cart 链接。站点鼓励用户定制、然后添加到购物车中。但是也允许用户跳过这些步骤,使用一组默认选项添加到购物车,在稍后进行定制。
图 2 展示了 Customize Me Now 1.0 的站点地图。它显示了用户的路径非常复杂。大多数屏幕都链接到至少 2 或 3 个其他屏幕。
图 2. Customize Me Now 1.0 站点地图 代码
Customize Me Now 的功能演示代码只提供了客户端部分:HTML CSS、JavaScript 代码和图像文件。在实际中,您显然还需要一个大型的服务器端组件:Microsoft® ASP.NET、Java 技术、Ruby on Rails、Django 或 PHP。但是 jQuery 的美妙之处在于,它纯粹关于客户端。您可以使用 Ajax 简化您的用户体验,无需服务器端组件。我没有提供任何服务器端代码,因为这些细节对于这个项目无关紧要。您只需知道当您打开一个 HTML 文件时,就会有一个 PHP 模板、一个 JavaServer Pages™ (JSP) 文件或任何其他将 HTML 发送到服务器的文件。
下载了 1.0 版应用程序的源代码之后,请看一下其中一个页面的标记。它应该与清单 1 类似。
清单 1. results.html 1.0 的 HTML 代码
<!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" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Customize Me Now: Search Results</title>
<link rel="stylesheet" type="text/css" href="../css/customizemenow.css"/>
</head>
<body id="CMN">
<!--[header goes here]-->
<div id="main">
<form method="GET" action="comparison.html">
<h1>Search Results</h1>
<div class="buttons">
<input class="button" type="submit" name="submitTop" id="submitTop"
value="Compare Selected Products" />
or <a href="index.html">search again</a>
</div>
<table class="searchResults">
<thead>
<tr>
<th>Product</th>
<th>Description</th>
<th>Options</th>
<th>Compare</th>
<th>Actions</th>
</tr>
</thead>
<tr>
<td class="name">
<a target="productPopup"
href="productPopup.html?product=A">Pizza</a>
</td>
<td class="desc">
A delicious Italian treat.<br />
[<a href="detail.html?product=A">full product details</a>]
</td>
<td class="options">
<ul>
<li>
<a target="optionsPopup"
href="optionsPopup.html?product=A">crust</a>
</li>
<li>
<a target="optionsPopup"
href="optionsPopup.html?product=A">cheese</a>
</li>
<li>
<a target="optionsPopup"
href="optionsPopup.html?product=A">toppings</a>
</li>
</ul>
</td>
<td class="action">
<input type="checkbox" target="productPopup" name="compareA"
id="compareA" value="true" checked="checked"/>
</td>
<td class="action">
<a class="button" href="customize.html?product=A">customize
product</a>
<a class="button" href="cart.html?product=A">add to cart with
default options</a>
</td>
</tr>
<!--[additional table rows go here]-->
</table>
<div>
<div class="buttons">
<input class="button" type="submit" name="submitBottom" id="submitBottom"
value="Compare Selected Products" />
or <a href="index.html">search again</a>
</div>
</form>
</div>
<!--[footer goes here]-->
</body>
</html>
改进应用程序
本文将介绍将 Customize Me Now 改进为 2.0 版的过程,本系列的第 2 部分还会进一步开发。
安装 jQuery 及其插件
要将一个 Ajax 行为层添加到您的站点,第一步是下载所有的开源库。如果您从下载小节下载了示例 2.0 应用程序,那么所有的库都应包含在其中了。如果想要下载这些库的最新版本,您可以从 下载 小节重新下载一次。
接下来,为 jQuery 和表单插件创建一个 /js 目录。注意,GreyBox、ThickBox 和 JTip 都需要自己的目录;它们与图片、CSS 文件和多个 JavaScript 库绑定在一起,所有这些都要求特定的目录结构。当链接到您的 CSS 和 .js 文件时,您必须包含一小段脚本,用来为 GreyBox 设置正确的根目录指针。这个指针必须是一个绝对目录路径,因此您可能需要在自己的代码中调整该值。完成之后,HTML 文件的头部元素应该与清单 2 类似。
清单 2. Customize Me Now 2.0 头部元素
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Customize Me Now: Shopping Cart</title>
<!--customizemenow assets-->
<link rel="stylesheet" type="text/css" href="../css/customizemenow.css"/>
<!--jquery assets-->
<script type="text/javascript" src="../js/jquery-1.2.1.min.js"></script>
<script type="text/javascript" src="../js/jquery.form.js"></script>
<!--thickbox assets-->
<script type="text/javascript" src="../thickbox/thickbox.js"></script>
<link rel="stylesheet" type="text/css" href="../thickbox/thickbox.css" />
<!--jtip assets-->
<script type="text/javascript" src="../jtip/scripts/jtip.js"></script>
<link rel="stylesheet" type="text/css" href="../jtip/css/jtip.css" />
<!--greybox assets-->
<script type="text/javascript">
/*this needs to be a non-relative reference*/
var GB_ROOT_DIR = "/customizemenow/2/0/greybox/";
</script>
<script type="text/javascript" src="../greybox/AJS.js"></script>
<script type="text/javascript" src="../greybox/AJS_fx.js"></script>
<script type="text/javascript" src="../greybox/gb_scripts.js"></script>
<link rel="stylesheet" type="text/css" href="../greybox/gb_styles.css" />
</head>
使用 ThickBox 和 jQuery 将辅助链接转换为 lightbox
由于 jQuery 及其插件遵循渐进增强的原则,您几乎不需要定制 JavaScript 代码来创建 Ajax 功能。您只需向现有的 HTML 标记添加特定的属性。您的 JavaScript 库将解析 Document Object Model (DOM) 以寻找这些特殊属性,然后向拥有这些属性的元素添加合适的行为。jQuery 支持这种编码风格,当所有标记被呈现时它将自动解析 DOM。如果研究一下 jQuery 插件的底层操作,您将会发现它们都将自己的事件模型委托给了核心 jQuery
对象及其 ready
方法。
现在您已经了解了背景,接下来使用 jQuery 简化您的导航。使用 ThickBox 将不属于主购买路径的任何页面由常规页面转换为 lightbox 页面。首先从 Product Details 页面开始,让您的用户可以从站点的任何地方查看产品,而无需离开主购买路径。
ThickBox 很容易调用。只需向每个相关的链接添加一些特殊属性:
thickbox
的 class
属性。这个特殊的 class
属性告诉 ThickBox 应该注意此元素。 一些 querystring 值:
KeepThis=true
和 TB_iframe=true
: 这些值告诉 ThickBox 在一个经过 iframe 处理的 lightbox 中呈现此链接。 height=400
: ThickBox 窗口的高度,以像素为单位。可以是任何值,本例中设置为 400。 width=600
: ThickBox 的宽度,以像素为单位。可以是任何值,本例中设置为 600。 由于您已经使用 querystring 将产品代码传递到 Product Details 页面,所以只需使用 ampersands (&) 将您的 ThickBox 值附加到现有 URL 上。将这些属性添加到 Product Details 链接之后,每个弹出链接应该与清单 3 类似。
清单 3. ThickBox 链接的 HTML 代码
<a
href="detail.html?product=A&KeepThis=true&TB_iframe=true&height=400&width=600"
class="thickbox">product details</a>
现在已经解决了 Product Details 页的问题,接下来改进 Comparison 页。到达此页的惟一途径是通过表单提交;用户必须使用复选框选择需要比较的产品。只能使用 jQuery Forms 呈现该表单提交的结果,jQuery Forms 是一个库,它将一些方便的方法和事件挂钩(hook)封装到 ajaxForm
对象中。使用 ajaxForm
和少量的定制 JavaScript 代码,您就可以直接调用 ThickBox 的 tb_show
方法。为此,将清单 4 中的脚本片段添加到 results.html 的头部。
清单 4. 从一个表单调用 ThickBox 的 JavaScript 代码
<script type="text/javascript">
/*create a thickbox for our form submittal*/
//when the document is ready
$(document).ready(function() {
//wrap form#comparison in an ajaxForm object
$('#comparison').ajaxForm({
//intercept the submit event with a callback
beforeSubmit: function(formData, jqForm, options) {
//serialize form data; append to the form action; tack on ThickBox params
var URL = jqForm[0].action + "?" + $.param(formData);
URL += "&KeepThis=true&TB_iframe=true&height=400&width=600";
//call ThickBox directly
var caption = null;
var imageGroup = false
tb_show(caption,URL,imageGroup);
//cancel the form submission by returning false
return false;
}
});
});
</script>
这段代码展示了 jQuery API 的简单性。通过少量代码,您就截取了一个 HTML 表单的正常提交,并执行了一些定制 JavaScript 代码。使用这种技术,您可以在允许提交表单之前执行定制验证逻辑,或者在表单提交之后触发一个定制事件。在本例中,您需要避免提交表单。相反,我们将手动 “伪造” 表单提交生成的 HTTP 请求,以便将表单的目标定向到 ThickBox 窗口。
用户并不知道所有这些后台操作。他们只知道在提交表单之后,将在模式窗口中看到结果。查看 Comparison 页面之后,用户可以关闭 ThickBox 窗口,然后返回来定制和购买产品。
使用 ThickBox 呈现 Product Details 和 Comparison 页面的惟一问题是,对于 ThickBox 窗口来说页面太大了。您可以更改传递给 ThickBox 的页面宽度和高度值,但是如果用户使用较小的视区又该怎么办?您不想让 ThickBox 覆盖整个窗口,更不想让它延伸到视区以外。您只需将 inline
的 class
添加到它们的主体标记,重新设置 details.html 和 comparison.html 的样式。然后,将清单 5 中的 CSS 声明添加到 customizemenow.css
。
清单 5. ThickBox 的 CSS
#CMN #main.inline {
width: 600px;
}
模式窗口实现过程的最后一步是,限制 Product Details 和 Comparison 页面中不希望与用户交互的元素。由于这些页面现在只是为了提供信息,所以无需包含操作链接和按钮。也无需显示您的导航栏或其他 chrome。
可以通过几种方式实现此目的。可以从页面删除这些元素,但是这不符合您的渐进增强策略。不支持 JavaScript 的用户转到这些页面之后无法退出或继续购买流程。您也可以将 querystring 参数附加到您的链接,让服务器端框架使用不同的模板呈现这些页面。在实际中,您很可能会这么做。但是此处还有另一种有效方式,这种方式只依赖客户端代码:老式的 <noscript>
标记。如果将每个元素都包装到一个 <noscript>
标记中,那么只有非 JavaScript 的用户代理能看见它:这些用户正是您所希望 的。
页眉和页脚的结果 HTML 代码与清单 6 中的代码类似。
清单 6. 用于 Customize Me Now 2.0 导航的 HTML 代码
<noscript>
<div id="footer" class="nav">
<<ul>
<li><a href="index.html">search&/a></li>
<li><a href="results.html">results</a></li>
<li><a href="detail.html">details</a></li>
<li><a href="comparison.html">compare</a></li>
<li><a href="customize.html">customize</a></li>
<li><a href="cart.html">cart</a></li>
<li><a href="checkout.html">checkout</a></li>
<li class="last"><a href="confirm.html">confirmation</a></li>
</ul>
</div>
</noscript>
Product Details 页面的主要内容 <div>
的 HTML 代码与清单 7 中的代码类似。
清单 7. details.html 2.0 的 HTML 代码
<div id="main" class="inline">
<form method="GET" action="customize.html">
<input type="hidden" name="product" id="product" value="A" />
<h1>Pizza: Product Details</h1>
<noscript>
<div class="buttons">
<input class="button" type="submit" name="submitTop" id="submitTop"
value="Customize Now" />
or <a href="cart.html?product=A">add to cart with default
options</a>
</div>
</noscript>
<!--[content goes here]-->
<noscript>
<div class="buttons">
<input class="button" type="submit" name="submitBottom" id="submitBottom"
value="Customize Now" />
or <a href="cart.html">add to cart with default options</a>
</div>
</noscript>
</form>
</div>
Comparison 页面的主要内容 <div>
的 HTML 代码与清单 8 中的代码类似。
清单 8. comparison.html 2.0 的 HTML 代码
<div id="main" class="inline">
<h1>Product Comparison</h1>
<table class="productComparison">
<thead>
<tr>
<th>Product</th>
<th>Pros</th>
<th>Cons</th>
<noscript>
<th>Actions</th>
</noscript>
</tr>
</thead>
<tr>
<td class="name">
<a class="jTip" name="About Pizza" id="pizza" target="productPopup"
href="productPopup.html?product=A">Pizza</a>
</td>
<td class="pros">
<ul>
<li>Great flavor.</li>
<li>Low cost.</li>
<li>Fun with friends.</li>
</ul>
</td>
<td class="cons">
<ul>
<li>Can make you fat.</li>
<li>Not very nutritious.</li>
</ul>
</td>
<noscript>
<td class="action">
<a class="button" href="customize.html?product=A">customize
product</a>
<a class="button" href="cart.html?product=A">add to cart with
default options</a>
</td>
</noscript>
</tr>
<!--[additional table rows here]-->
</table>
</div>
在浏览器中查看 Customize Me Now 2.0 Search Results 页面并启动 Product Details 或 Comparison 页面,您可以看到所有的改进结果。其结果应该与图 3 类似。
图 3. 运行中的 ThickBox