Ajax 改造:用 jQuery 和 Ajax 表单改造现有站点
现在就可以开始构建 Customize Me Now 2.2 了。通过将结帐过程转变成一个单一屏幕的界面,可以减少服务器往返带来的延迟,还可以让步骤间的转变不再如此让人迷惑。完成之后,转到登录和折扣屏幕的过程将很直观而不会让人迷路。
这次改造所需工具包括在之前的文章中用到过的流行 Ajax 库 jQuery 和其他两个您很熟悉的插件:
- jQuery UI Tabs,在第 3 部分用过,可以将一个无序列表转变成一个选项卡界面,而且可以用来自现有的 Document Object Model (DOM) 对象或 Ajax 调用的内容填充每个选项卡。这里,我们使用它来将 7 个单独的 HTML 文件转变成单一屏幕的界面。在上次部署此插件时,我们更改了所包括的背景图像以改进其外观。这次我们还将使用这个修改后的同一个图像。
- jQuery Form,在第 1 部分和第 2 部分用过,提供了一些方法,可通过 Ajax 提交表单以及以多种方式处理结果。这里,我们使用它来控制单一屏幕的结帐过程各步骤间的流程。
这次改造的重头戏都发生在一个页面:checkout.html。要将此文件准备好,请下载 jQuery 的 JavaScript 和 CSS 文件及其插件并在 HTML 文件头部引用它们。结果应该如清单 1 所示:
<!--jquery assets--> <script type="text/javascript" src="../js/jquery-1.2.3.min.js"></script> <script type="text/javascript" src="../js/jquery.form.js"></script> <!--jquery.ui.tabs assets--> <script type="text/javascript" src="../ui.tabs/ui.tabs.pack.js"></script> <link rel="stylesheet" href="../ui.tabs/ui.tabs.css" type="text/css" media="print, projection, screen"> |
和本系列 第 3 部分 一样,还需要创建现有的 HTML 文件的一些 HTML 片段版本。此步骤十分必要,因为 Ajax 响应通常需要忽略原本充当页面主要内容的页头、页脚和其他外部元素。HTML 文件的整页格式和片段格式都可以从相同的服务器端模板引擎获得。这里,我们如法炮制,我们将几个文件复制过来并改成新文件名,而保留原始的文件原样不动。这样操作之后,得到的新文件如下:
- login-fragment.html
- checkout1-fragment.html
- checkout2-fragment.html
- checkout3-fragment.html
- checkout4-fragment.html
- checkout4a-fragment.html
接下来,打开这些文件并去掉除主表单元素及其子元素之外的所有内容。完成后,login-fragment.html 的全部内容类似清单 2 ,而每个后续文件也都遵循相同的模式:
<form method="GET" action="checkout1.html" class="checkout" id="lform"> <h2>Step 1: Personal Info</h2> <h3>Login</h3> <div> <label> Email Address <input type="text" name="email" id="email"/> </label> <label> Password <input type="password" name="password" id="password"/> </label> </div> <div> <input class="button" type="submit" name="submit" id="submit" value="submit" /> </div> </form> |
有了所需的全部文件后,就可以开始进行 Ajax 改造的真正工作了。大多数的代码更改都发生在 checkout.html 之内,正如您所见,该文件没有复制并改变成片段版本。这是因为 checkout.html 的现有版本既充当新选项卡界面的中心枢纽,又是现有界面的起点,要求即使在 JavaScript 功能不可用时,用户仍能看到。
借助 jQuery UI Tabs 将单独的页面转变成选项卡
在 checkout.html 内,需要创建一些 div 元素来保存将要转变成选项卡格式的内容。每个 div 都接收 "tabContent" 的 class 属性以便对其应用样式,而每个元素都有惟一的 id 属性以便能在 JavaScript 代码内对其进行对象引用。第 1 个 div 元素包围的是 checkout.html 的现有内容。3 个额外的空 div 元素被添加在其下作为之后将要通过 Ajax 获取的内容的占位符。完成之后,HTML 代码应类似清单 3:
<div id="personalInfo" class="tabContent"> <h2>Step 1: Personal Info</h2> <div class="buttons"> <a class="button" href="checkout1.html" id="checkoutAsGuest"> check out as guest </a> <a class="button" href="login.html" id="login">log in</a> </div> <div class="fakeForm"> <p>[long, boring playback of order details]</p> </div> </div> <div id="shippingDetails" class="tabContent"></div> <div id="billingDetails" class="tabContent"></div> <div id="orderReview" class="tabContent"></div> |
您可能会注意到我们只为结帐过程的 5 步骤中的 4 个步骤创建了 div 元素。没有关系。步骤 5 Confirmation 是个特殊的情况。之所以让它成为 5 步骤中的一个步骤是为了方便用户知道自己在过程中所处的位置,但步骤 5 实际发生在结帐过程完成之后。所以,它在新的一个页面打开而不是选项卡界面,所以无需为之创建占位符。
此外,还要确保刚刚创建的容器 div 被适当地样式化。加上边界和间距以使处于选项卡下的这些容器有较好的外观。另外,要压缩它们中的 h2 元素,因为,选项卡上的标签会过多的呈现它们。幸运的是,您已经在本系列中的 第 3 部分 创建了满足这两个需求的样式规则,所以,只需确保这些规则被加入到 customizemenow.css 的未尾就可以了。结果如请单 4 所示:
#CMN .tabContent {
padding: 14px;
border: 1px solid #97a5b0;
}
#CMN .tabContent h2{
display: none;
}
|
至此,我们已为选项卡内容创建并样式化了 div 包装器,下面需要创建选项卡本身了。您可能还记得在 第 3 部分,jQuery UI Tabs 从一个无序列表中构造了它的选项卡式的界面。我们已经有了一个显示 breadcrumb navigation 的 ul 元素;如清单 5 所示:
清单 5. breadcrumb navigation 的 HTML
<div class="breadcrumb nav"> <ul> <li class="current">1. Personal Info</li> <li>2. Shipping Details</li> <li>3. Billing Details</li> <li>4. Order Review</li> <li class="last">5. Confirmation</li> </ul> </div> |
然而,这个标记与 jQuery UI Tabs 所期望的 HTML 结构之间存在着一些差异。因此,应该为选项卡创建一个备用的 ul 元素,然后用 CSS 和 JavaScript 代码来切换这两个列表的可视性。默认的模式是显示 breadcrumb navigation 并隐藏选项卡; 这确保了没有 JavaScript 功能的用户也能看到他们经常看到的相同的 Web 1.0 界面。也可以使用 JavaScript 代码来隐藏这个 breadcrumb 列表,而为使用 Ajax 结帐界面的那些启用了 JavaScript 功能的用户显示选项卡列表。
这些选项卡的 ul 元素应该类似清单 6:
<ul class="navTabs"> <li><a id="tab0" href="#personalInfo"> <span>1. Personal Info</span></a> </li> <li><a id="tab1" href="#shippingDetails"> <span>2. Shipping Details</span></a> </li> <li><a id="tab2" href="#billingDetails"> <span>3. Billing Details</span> </a></li> <li><a id="tab3" href="#orderReview"> <span>4. Order Review</span> </a></li> <li class="last"><a id="tab4" href="#"> <span>5. Confirmation</span> </a></li> </ul> |
注意到选项卡内链接元素的这些 URL 对应于之前创建的内容 div 的名称。无需任何相关内容,Confirmation 选项卡只需一个 hash 符号来表示其伪 URL。
用来隐藏新 ul 元素的 CSS 处于 checkout.html 主体的 noscript 块。正如您所注意到的,对选项卡使用了与本系列 第 3 部分 相同的策略。现在,正如在第 3 部分一样,添加几个额外的 noscript 样式来协助没有启用 JavaScript 功能的那些用户。实际上,改变了 清单 4 中创建的样式规则。处理完 noscript 样式块后,它应该类似清单 7:
<noscript>
<style type="text/css">
/*don't show tabs when JS disabled*/
#CMN .navTabs {
display: none;
}
/*without the tab box, disable border+padding*/
#CMN .tabContent {
padding: 0;
border: 0;
}
/*without the tab labels, stop hiding h2s*/
#CMN .tabContent h2 {
display: block;
}
</style>
</noscript>
|
接下来的部分十分有趣。现在,可以借助一些定制 JavaScript 代码来将所有东西集中在一起。将所有代码捆绑到 jQuery 的定制 document.ready 事件,该事件在 DOM 可用于浏览器的 JavaScript 引擎时触发。这确保了所有需要处理的 DOM 元素都已准备就绪。
要创建这个复杂的事件处理程序,仅需向 checkout.html 的 head 元素的底部添加一个脚本块,如清单 8 所示:
<script type="text/javascript">
//when the document is ready
$(document).ready(function() {
/* hide the breadcrumbs ul and show the tab ul */
$('div.breadcrumb').hide();
$('ul.navTabs').show();
/*turn the newly visible ul into tabs*/
var tabSet = $('ul.navTabs').eq(0).tabs(
{
/*apply a nice visual effect to tab activation*/
fx: { height: 'toggle', opacity: 'toggle' },
/*disable all but the first tab by default*/
disabled: [1,2,3,4]
}
).bind('select.ui-tabs', function(event, ui) {
/*
ensure that each time a new tab is activated
all subsequent tabs are disabled. This will
prevent users from jumping around in the process
*/
var currentTab = parseInt(ui.tab.id.substring(3));
var tabSetLength = 5;//a necessary hack
for (var i = 0; i < tabSetLength; i++) {
if (i > currentTab) {
tabSet.tabs("disable", i);
}
}
});
/*
bind the checkout and login links to new click
handlers and cancel their original behavior.
now, these links will open the fragment versions
of their original targets inside the first tab.
*/
$("#checkoutAsGuest,#login").click(function() {
$('#personalInfo')
.load(
$(this)
.attr("href")
.replace(".html", "-fragment.html")
)
;
return false;
});
});
</script>
|
正如 清单 8 中的注释所表明的,我们借助 jQuery 的力量来将原始的标记转变成其新的选项卡格式。我们隐藏了页面不再需要的元素,展示需要的元素并告知 jQuery UI Tabs 转变这些元素的外观和行为。传递给 tabs 方法的选项包使用了与 第 3 部分 相同的视觉效果,但额外的一个参数可用来禁用除第一个选项卡之外的所有选项卡。这能让结帐过程成为单向过程,用户如果没能按顺序完成每个步骤,他将无法向前跳转。在本文后面,我们还将编写代码来在需要的时候逐个启用和禁用这些选项卡。
jQuery UI Tabs 还提供了选项卡式界面的生命周期内的一些定制事件挂钩。与诸如 onclick 和 onmouseover 这样的内置 DOM 事件类似,定制事件也能够让回调处理程序应用于它们。惟一的不同点是事件本身由 jQuery 及其插件而不是浏览器定义。可以使用这些定制事件处理程序中的一个来进一步控制在给定的时间哪个选项卡被启用或禁用。
每当一个新的选项卡被激活,一个名为 select.ui-tabs 事件就会抛出(这个事件的名称在 jQuery UI Tabs 的后续版本中有所更改,所以,如果遇到问题,可以去查看相关文档)。通过在新创建的选项卡组上调用 jQuery 的 bind 方法,可以给这个定制事件附加一个处理程序。这个处理程序遍历整个选项卡组并会禁用当前选项卡后面的所有选项卡。然而,这个 select.ui-tabs 事件处理程序并不能访问整个选项卡组,而只能访问所选中的选项卡。因此,您不得不硬编码对选项卡集的引用以及其中的选项卡数。
连接好其他选项卡的行为后,需要告诉第一个选项卡中的内容要做些什么。这包括更改标签为 log in 和 check out as guest 的两个链接的行为,以便它们通过 Ajax 在当前选项卡中加载它们的目标页面。原始的链接指向整个 HTML 页面,而我们只需要前面创建的 HTML 片段。因此要使用 jQuery 来获取两个链接的对象引用并用 click 事件处理程序覆盖它们的默认行为。还需要获取每个链接的 href 属性,使其转而指向一个片段文件并通过 Ajax 获取结果 URL。最后,在 Personal Info 选项卡内呈现响应。




