Ajax による改良: 第 4 回 jQuery および Ajax フォームで既存のサイトを改良する

オープンソースのツールを使って、複数ステップのプロセスを 1 画面で構成される合理的なインターフェースへ変換する

Ajax の手法は大規模な商用 Web アプリケーションの様相を一変させましたが、規模の小さな多くの Web サイトにはユーザー・インターフェース (UI) をまるごと一晩にしてリビルドするだけのリソースはありません。しかし Ajax による新しい機能を利用すると、実際のインターフェースの問題が解決され、ユーザー・エクスペリエンスも改善されることで、新しい機能にかかるコストが妥当なものであることが証明されるはずです。UI をオープンソースによるクライアント・サイドのライブラリーを使って徐々に最新のものにする方法は、この連載ですでに説明しました。今回の記事では、複数のステップからなる購入プロセスを、Ajax の手法を使って複数の連続したフォームから 1 画面で構成されるインターフェースに変える方法を学んでください。この変換では、Progressive Enhancement (漸進的な機能拡張) の原則を利用して、あらゆる類のユーザー・エージェントが引き続きサイトにアクセスできることを保証します。

Brian J. Dillard, VP, Ajax Development, Pathfinder Development

Brian DillardWeb 開発者としての 12 年間、Brian J. Dillard は Orbitz Worldwide、Reflect True Custom Beauty、Archipelago LLC、United Airlines といったさまざまな会社のためにリッチなユーザー・インターフェースをビルドしてきました。現在、シカゴの Pathfinder Development で RIA の熱烈な支持者として活躍する彼は、多様なクライアントのためにリッチ・インターネット・アプリケーションをビルドする傍ら、オープンソース・プロジェクトに参加し、Agile Ajax ブログにも投稿しています。彼がプロジェクト・リーダーを務める Really Simple History は、人気の高い Ajax の履歴およびブックマーク・ライブラリーです。



2008年 7月 29日

この記事について

この記事では、Web 1.0 のショッピング・サイトを Ajax の手法を使って改良する手順を紹介します。サンプル・アプリケーションを「改良する前」と「改良した後」のソース・コードはダウンロードすることができます。また、この 2 つのバージョンが実際に動作する様子を著者の Web サーバーで確認することもできます。Ajax の手法とベスト・プラクティスに加え、さらに Ajax が Progressive Enhancement (漸進的な機能拡張) の原則によってユーザー・エクスペリエンスをどのように改善するかについても説明します。

この記事では、読者が HTML および CSS について十分理解していること、そして少なくとも JavaScript および Ajax プログラミング手法についての基礎知識があることを前提とします。サンプル・アプリケーションはクライアント・サイドのコードのみを使用して構築しているため、ここで紹介する手法はサーバー・サイドのどのアプリケーション・フレームワークにも適応できるはずです。すべての Ajax アプリケーションの例に漏れず、サンプル・コードはデスクトップ上のファイルとしてではなく、Web サーバーから実行する必要があります。あるいは、ソース・コードを追いかけ、著者の Web サーバーでサンプル・サイトの動作を確認するのでも構いません。


第 1 回から第 3 回までの復習

この連載の第 1 回第 2 回ではサンプル・アプリケーション、Customize Me Now について紹介し、このアプリケーションを Web 1.0 バージョンから Ajax で強化した Web 2.0 バージョンに改良するプロセスを開始しました。jQuery JavaScript フレームワークとその他のオープンソースのライブラリーを使って、ポップアップ、オフサイト・リンク、ナビゲーションの脇道をモーダル・ダイアログ、ツールチップ、ライトボックスに置き換えることで、Customize Me Now のユーザー・フローを簡素化しました。第 3 回ではさらに改良を加え、Ajax/DHTML タブ内の長々としたコンテンツをラップし、クリックしてから表示されるまで待たされるフォト・ページを、機敏に表示が切り替わる画像カルーセルに置き換えました。

第 4 回での目標

今回は、複数のページにまたがるフォームを Ajax タブに変えることによって、複雑なプロセスを簡素化します。その事例として使うのは、サンプル・ショッピング・サイトでの購入プロセスです。Ajax を使用しない複数ページのフォームは長たらしく面倒で、カスタマーの買う気をそいでしまうことがあります。Ajax で改良すれば、複雑な購入プロセスでさえも、気の利いたわかりやすいプロセスに見せることができます (ただし、ユーザー・インターフェースの構造は慎重に考慮する必要があります)。この手法が効果を発揮するのは e-コマース・サイトだけではありません。複数ステップのプロセスを完了するためにユーザーが相互に関連した一連のフォームに入力しなければならない場合には例外なく、同じ原則が当てはまります。

この記事のコンセプトを理解するには、まず Customize Me Now 1.2 を参照してください (「ダウンロード」を参照)。これは、元のサンプル・サイトを多少変更した、Ajax を使用していないバージョンです。この 1.2 に変更を加えながら、連載全体を通して説明するすべての変更を組み込んだ Customize Me Now 2.2 を作成します。

e-コマースでの購入プロセス: Web 1.0 バージョン

オンライン・ショッピングが大好きなカスタマーでさえも、購入プロセスに嫌気がさすことはよくあります。その原因は山とあります。

  • 多くの場合、プロセスに必要なステップの数が明確になっていません。
  • それぞれのステップにどれだけの時間がかかるのかも不明です。
  • ユーザーが特定の質問にどう答えるかによって、アプリケーションの関係のない部分に迂回させられることがあります。配送オプションを選択したり、値引きコードを適用したり、あるいはログインするだけでも、それが原因となって一見単純そうなプロセスに予想以上の時間がかかる場合があります。
  • サイトの開発者がプロセスのセキュアな部分を慎重にコーディングしない限り、暗号に関するセキュリティー警告がユーザーを苛立たせることになります。
  • エラー・メッセージの説明が足りなかったり、エラーの手掛かりに一貫性がなかったりすることが原因となって、ユーザーがどこでエラーが発生したのかを把握しにくいことがあります。
  • 検証ルーチンのコードの出来が良くないせいで、苛立たしい無限ループに陥ることがあります。無限ループの発端は、サーバーにアクセスするとクレジット・カード番号が削除されるために再入力しなければならないこと、チェック・ボックスに入れたチェック・マークがいつの間にか外されてしまうことなど、さまざまです。

e-コマースでの購入プロセス: Web 2.0 バージョン

既存の Web サイトを Ajax で改良するために時間と開発費をかける際には、ユーザー・エクスペリエンスを改善することが主要な目標の 1 つとなるはずです。Ajax は、利用者が購入プロセスに関して嫌悪するあらゆることを解決できるわけではありませんが、少なくとも以下の 3 つの点で確実に成果を上げます。

  • プロセスを完了するために必要なステップの数が明確になること
  • ステップ間の遷移が速くなること
  • ユーザーがログインしたり、値引きコードを入力したりするとき、あるいは他にも「理想のパス」から外れなければならない場合の動作が単純で一貫したものになること

サンプル・アプリケーション: Customize Me Now 1.2

Web ブラウザーで Customize Me Now 1.2 を見てみると、多くのサイトと同じく、購入プロセスが比較的複雑だという印象を覚えるはずです。ここに表示されているパンくずリストによるナビゲーションでは、購入プロセスは以下の 5 つのステップで構成されています。

  1. 個人情報 (Personal Info)
  2. 配送の詳細 (Shipping Details)
  3. 支払いの詳細 (Billing Details)
  4. 注文内容の確認 (Order Review )
  5. 確認 (Confirmation )

それにもかかわらず、実際のプロセスはもっと複雑です。ステップ 1 を始める前から、ユーザーはゲストとして購入プロセスを開始するか、あるいはログインするかを決めなければなりません。前者を選んでゲストとして購入プロセスを開始すると、ステップ 1 に直接案内され、そこで名前と連絡先情報の入力を求められます。一方、後者を選んでログインすることにした場合は、ログイン画面に進んでユーザー名とパスワードを入力した上で、ステップ 1 に戻って以前に入力した連絡先情報を確認しなければならなくなります。

ステップ 1 を終えると、プロセスはかなり直線的になりますが、ステップ 4 の「注文内容の確認 (Order Review )」に達すると、選択可能な別の迂回路が提示されます。この場合の迂回先は、「値引き (Apply Discount)」ページです。

このプロセスでは迂回路も含め、プロセスのどの段階にいるのかを把握するためのツールはたった 1 つしかありません。それは、前述のパンくずリストによるナビゲーションです。このナビゲーションでクリックできるのは、すでに完了したブレッドグラム項目のみです。つまり、プロセスの前のステップに戻ることはできても、先のステップにジャンプする手段はありません。図 1 に、このパンくずリストがどのように表示されるかを示します。

図 1. Customize Me Now 1.2 のパンくずリスト
Customize Me Now 1.2 のパンくずリスト

このパンくずリストによるナビゲーションは、ある程度は便利なものの、縦棒で区切られたリストとしてスタイル設定されているため、多くのユーザーはその存在に気付かないことさえあります。また、ユーザーが選択可能な迂回路は正確には表示されません。実際には、ログインはステップ 1 のサブステップで、値引きコードはステップ 4 のサブステップです。さらに、このような迂回路のそれぞれには、ユーザーを当初の場所に戻すためにサーバーとの 2 回の往復が必要になるため、サーバーにアクセスするたびにプロセスは長くなり、新しいページがロードされることによってユーザーを混乱させることになります。

今までのすべての Customize Me Now のバージョンと同様、実際にはおそらくヘッダーとフッターにナビゲーションが表示されることはありません。ここにナビゲーションを表示しているのは、読者がこの記事を読むなかで、サンプル・アプリケーション内のさまざまなページに簡単にジャンプできるようにしたいという理由からだけです。Customize Me Now 1.2 サンプル・コードと実際の e-コマース・アプリケーションとの違いは、これだけではありません。具体的には、サンプル・コードでは SSL (Secure Sockets Layer) 暗号化を使用していないこと、そして HTTP (Hypertext Transfer Protocol) 接続のどちらの側でも検証を行わないことが挙げられます。さらに、プロセスの各ステップは静的なので、例えばユーザーが配送先の住所を請求書の送付先とするチェック・ボックスにチェック・マークを付けたとしても、請求書送付先の住所が事前に入力されることはありません。実際に稼動している e-コマース・サイトでは、この機能をサーバー・サイドのコードによって実現するはずですが、この記事では、クライアント・サイドのコードによって、実際に起こるであろう複雑な内容を示唆するだけにとどめています。

購入プロセスの改良: 最初のステップ

ここからはいよいよ、Customize Me Now 2.2 の作成に取り掛かります。購入プロセスを単一画面のインターフェースに変えることによって、サーバーとのやりとりに伴う待ち時間を減らし、ステップ間の遷移による混乱を少なくします。単一画面へと変更した暁には、ログイン画面と値引き画面へのアクセスは、複雑な迂回ではなく自然なプロセスの一部となるはずです。

JavaScript ライブラリーに接続する

この改良では、これまでの記事でも使用した人気の Ajax ライブラリー、jQueryの他、以下の 2 つのお馴染みのプラグインを使用します。

  • jQuery UI Tabs。第 3 回で使用したこのプラグインは、順序なしリストをタブ・インターフェースに変換し、各タブに既存の DOM (Document Object Model) 要素からのコンテンツ、または Ajax 呼び出しからのコンテンツを設定します。今回はこのプラグインを使用して、7 つの個別 HTML ファイルを単一画面のインターフェースへと変えます。前回このプラグインを使用したときに、付属の背景画像を変更して画面の角丸の外観を改善しました。今回も、前回で変更した同じ画像を使用してください。
  • jQuery Form。第 1 回と第 2 回で使用したプラグインで、Ajax によってフォームをサブミットするメソッド、そしてその結果をさまざまな方法で操作するメソッドを提供します。今回はこのプラグインを使用して、単一画面の購入プロセスでのステップのフローを制御します。

この改良作業での力仕事はすべて、1 つのページ上つまり checkout.html 上で行われます。このファイルを準備するには、jQuery とそのプラグインに対応する JavaScript ファイルと CSS ファイルをダウンロードして、HTML ファイルの先頭でこの 2 つのファイルを参照してください。その結果、ファイルの先頭はリスト 1 のようになります。

リスト 1. JavaScript ライブラリーのリンク
<!--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">

HTML フラグメントを作成する

この連載の第 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 のようになります。その他のファイルもこれと同じパターンです。

リスト 2. HTML フラグメント・ファイルのフォーマット
<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 には class 属性として "tabContent" が指定されるため、これにスタイルを適用することができます。さらに各 div 要素には固有の id 属性が指定されるので、JavaScript コードの中でそれぞれの div 要素をオブジェクト参照することができます。最初の div 要素は checkout.html の既存のコンテンツをラップします。その下には 3 つの空の div 要素が追加され、後で Ajax によってフェッチされるコンテンツのプレースホルダーとなります。この作業が完了すると、HTML コードはリスト 3 のようになります。

リスト 3. タブ・コンテンツ・ラッパー用の HTML
<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 つのステップのうち、div を作成したのは 4 つのステップに対してのみです。それでも心配には及びません。ステップ 5 の「確認 (Confirmation)」は特殊なケースだからです。ユーザーが購入プロセスのどの段階にいるのか常に意識できるように、5 つのステップのうちの 1 つとして「確認 (Confirmation)」ステップを表示したいと思うかもしれませんが、ステップ 5 は実際には購入プロセスが完了した後に起こるため、タブ・インターフェースのなかではなく新しいページで開きます。したがって、ステップ 5 用にプレースホルダーを作成する必要はありません。

また、作成したコンテナー div には、適切なスタイルが設定されるようにする必要もあります。タブの下に適切に表示されるように、コンテナーにはボーダーとパディングを追加します。さらにコンテナーに含まれる h2 要素は表示されないようにしなければなりません。タブ上のラベルが、この要素を重複してレンダリングするためです。幸い、連載の第 3 回でこの 2 つのニーズを満たすスタイル・ルールをすでに作成しているので、今回はこれらのルールが customizemenow.css の一番下に追加されていることを確認するだけで十分です。結果はリスト 4 のようになります。

リスト 4. タブ・コンテンツにスタイルを設定する CSS
#CMN .tabContent {
	padding: 14px;
	border: 1px solid #97a5b0;
}
#CMN .tabContent h2{
	display: none;
}

タブ付きコンテンツの div ラッパーを作成し、スタイルを設定したところで、次はタブ自体を作成する必要があります。第 3 回で覚えているかもしれませんが、jQuery UI Tabs はそのタブ・インターフェースを順序なしリストから構成します。パンくずリストによるナビゲーションを表示する ul 要素はすでにあります。この要素の内容は、リスト 5 のようなものです。

リスト 5. パンくずリストによるナビゲーションのための 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 コードを使って 2 つのリストの表示を切り替えられるようにします。デフォルト・モードではパンくずリストによるナビゲーションを表示し、タブを非表示にします。こうすれば、JavaScript 機能を使用しないユーザーに対しては、いつもの見慣れた Web 1.0 インターフェースが確実に表示されるようになります。一方、JavaScript が有効で、Ajax による購入プロセスのインターフェースが表示されるユーザーに対しては、パンくずリストを非表示にして代わりにタブを表示する JavaScript コードを使用することができます。

タブの ul 要素は、リスト 6 のとおりです。

リスト 6. タブ用の HTML
<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 (確認) タブには、そのダミー URL としてのハッシュ記号さえあれば十分です。

新しい ul 要素を非表示にするための CSS は、checkout.html の body 内にある noscript ブロックに配置します。覚えているかもしれませんが、連載の第 3 回でこれと同様の方法をタブに対して適用しました。そのときと同様に、ここでも JavaScript 機能が有効でないユーザーを支援するための noscript スタイルをいくつか追加します。基本的には、リスト 4 で作成したスタイル・ルールを逆にするということです。作業後の noscript スタイル・ブロックは、リスト 7 のようになります。

リスト 7. Noscript CSS
<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 コードで 1 つにつなげる段階にきました。すべてのコードは jQuery のカスタム document.ready イベントにバインドします。これは、ブラウザーの JavaScript エンジンで DOM が使用可能になると起動されるイベントなので、操作する必要のあるすべての DOM 要素が確実に使用できる状態になるというわけです。

この複雑なイベント・ハンドラーを作成するには、script ブロックを checkout.html の head 要素の最後に追加すればよいだけです。リスト 8 に一例を記載します。

リスト 8. タブを作成するための JavaScript コード
<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 回で使用したビジュアル・エフェクトと同じエフェクトを用いていますが、追加パラメーターによって、最初のタブ以外のすべてを無効にすることができます。このようにして購入プロセスを一方向のプロセスにすれば、ユーザーは各ステップを完了するまでその先のステップには進めなくなることになります。この後、無効にしたタブを必要に応じて 1 つずつ有効にするためのコードを作成します。

jQuery UI Tabs はまた、タブ・インターフェースのライフサイクル期間中にカスタム・イベント・フックを提供します。onclickonmouseover などの組み込み DOM イベントと同じく、カスタム・イベントにはコールバック・ハンドラーを適用することができます。DOM イベントと異なる点は、イベント自体はブラウザーではなく、jQuery とそのプラグインによって定義されるということだけです。これらのカスタム・イベント・ハンドラーのいずれかを使用すれば、いつ、どのタブを有効または無効にするかを設定することができます。

新しいタブが有効になるごとに、select.ui-tabs というイベントがスローされます (このイベントの名前は jQuery UI Tabs の以降のバージョンでは変更されているため、問題が発生した場合には資料を確認してください)。このカスタム・イベントにハンドラーを接続するには、新しく作成されたタブのセットで jQuery の bind メソッドを呼び出します。接続されたハンドラーはタブ・セット全体を循環し、プロセスで現在選択されているタブよりも後にあるタブのそれぞれを無効に設定します。残念ながら、select.ui-tabs イベント・ハンドラーはタブのセット全体にアクセスすることはできません。アクセスできるのは、選択された個々のタブのみです。そのため、タブ・コレクションへの参照と、セットに含まれるタブの数をハード・コーディングする必要があります。

その他のタブの振る舞いを設定したら、最初のタブのコンテンツに実行内容を指示します。それには、log in および check out as guest のラベルが付いた各リンクの振る舞いを変更して、現行のタブ内でそれぞれのターゲットを Ajax によってロードするようにします。元のリンクが指しているのは HTML ページ全体ですが、ここで必要なのは前に作成した HTML フラグメントだけです。そのため、まずは jQuery を使用してこの 2 つのリンクへのオブジェクト参照を取得し、それぞれのデフォルトの振る舞いを click イベント・ハンドラーでオーバーライドします。次に、各リンクの href 属性を取得してフラグメント・ファイルを指すように変更し、変更後の URL を Ajax によって取得します。そして最後に、Personal Info タブ内でレスポンスをレンダリングします。

これで、5 つのステップからなる購入プロセスのステップ 1 は構築できました。ブラウザーで Checkout (購入) ページにアクセスすると、図 2 のようなページが表示されます。

図 2. Customize Me Now 2.2 の購入プロセスのステップ 1: log in または check out as guest サブステップ
Customize Me Now 2.2 の購入プロセスのステップ 1: log in または check out as guest サブステップ

check out as guest リンクを選択すると、Personal Info タブのコンテンツが実際の個人情報フォームで更新されます (図 3 を参照)。

図 3. Customize Me Now 2.2 の購入プロセスのステップ 1: Personal Info フォーム
Customize Me Now 2.2 の購入プロセスのステップ 1: Personal Info フォーム

一方、log in リンクをクリックした場合には、Personal Info タブのコンテンツがログイン・フォームで更新されます (図 4 を参照)。

図 4. Customize Me Now 2.2 の購入プロセスのステップ 1: Login フォーム
Customize Me Now 2.2 の購入プロセスのステップ 1: Login フォーム

改良作業のこの時点でログイン・フォームまたは個人情報フォームを飛び越そうとすると、タブ・インターフェースは簡単に壊れてしまいます。今の時点ではタブ内でコンテンツをロードし続けるのではなく、フラグメントにはなっていない元のページ全体を更新しなければなりません。これは、個々の HTML フラグメント・ファイルにはまだ、タブ・インフラストラクチャー内でどのように機能するかを指示していないためです。この最後のタスクを完了するには、もう 1 つのプラグイン、jQuery Form が必要になります。

jQuery Form を使用してステップバイステップのフローを制御する

log in リンクと check out as guest リンクは Ajax タブに簡単にリダイレクトできましたが、残りの Checkout (購入) ページに含まれる HTML フォームのデフォルトの振る舞いは、そう簡単にはオーバーライドすることはできません。そこで必要な機能を提供してくれるのが、第 1 回でも説明した jQuery Form です。Ajax による改良を完成させるための秘訣は、このプラグインを使って古臭いフォームを見事な Ajax ウィジェットに変換することです。

この変換を行うため、各 HTML フラグメント・ファイルの一番下に script ブロックを追加し、フラグメントのフォーム・サブミットの部分を作成しなおします。フラグメントは Ajax によってロードされ、それぞれのスクリプトは checkout.html のコンテキスト内で実行されるため、フラグメントを外部スクリプト・ファイルにリンクさせる必要はありません。スクリプト・ファイルはすでに checkout.html に組み込まれています。

まずは、ログイン・フォームから取り掛かります。以前、このフォームはユーザーが Ajax を使ってアクセスできるようにしてあります。そのため、ここで必要となるのは、ハンドラーをフォームの submit イベントに接続してフォームのサブミットにも Ajax が使用されるようにすることだけです。複数のフォームを同じ HTML シェル内でロードすることになるため、各フォームには固有の HTML id 属性を指定し、スクリプトがそれぞれを区別できるようにしなければなりません。フラグメント・ファイルの HTML を見ると、この部分はすでに対応済みであることがわかります。login-fragment.html に含まれるフォームには lform という id が設定され、checkout1-fragment.html に含まれるフォームには pform という id が設定されているといった具合です。

(矛盾しないように、個々のフォーム・フィールドにも固有の ID を指定する必要がありますが、これについてもサンプル・コードではすでに対応済みです。)

login-fragment.html のサブミット・ハンドラーはリスト 9 のようになります。

リスト 9. ログイン・ページの JavaScript サブミット・ハンドラー
<script type="text/javascript">
// bind the form and provide a callback function
$('#lform').submit(function() { 

	//submit the form via ajax
	$(this).ajaxSubmit({
		target:     '#personalInfo', 
		url:        'checkout1-fragment.html', 
		success:    function() { 
			var tabSet = $('ul.navTabs');
			tabSet.tabs("enable", 0);
			tabSet.tabs("select", 0);
		}
	});

	//don't actually submit the form normally
	return false; 
});
</script>

上記のハンドラーは関連するフォームへのオブジェクト参照を取得し、Ajax で元のフローに戻るために ajaxSubmit メソッドを使用しています。ajaxSubmit に渡すオプションのバンドルが指示する内容は、以下のとおりです。

  • フォームの値をサブミットする宛先 (checkout1-fragment.html。これは、個人情報フォームが含まれるファイルです。)
  • レスポンスのロード先 (最初のタブの div 要素。この要素には、personalInfoid 属性があります。)
  • Ajax 呼び出しが成功した場合のアクション (インデックスが 0 として識別される最初のタブを使用可能にして選択します。)

最後に false を返すことによって、Ajax を使用しない通常のフォーム・サブミットをキャンセルします。

改良がどれだけ進んでいるかを確認するため、ブラウザーで購入プロセスを新たに開始してください。log in リンクをクリックし、ログイン・フォームをサブミットすると、最初のタブに個人情報フォームがロードされるはずです。このように、個人情報フォームには購入プロセスの開始直後にアクセスすることも、ログイン・ページに迂回してからアクセスすることもできるようになりました (もちろん実際には、ログイン後に個人情報フォームにアクセスした場合には、フォームには事前に入力された情報が表示されるはずです)。

まだ、これで終わりというわけではありません。最初のタブから完全に離れて、それ以降のタブに進むための手段を作成する必要があります。そのため今度は、フォーム・サブミット・ハンドラーを個人情報フォームが含まれる checkout1-fragment.html に追加します。このハンドラーはログイン・フォームの場合のハンドラーと同様ですが、結果をタブにロードし、タブを有効にして、そのタブを選択する、というすべてのアクションを、最初のタブではなく 2 番目のタブで実行します。したがって、オプション・バンドルが指すのは idshippingDetails に設定された div となり、対応するタブの (0 を基準とする) インデックスは 1 となります。この作業が終わると、ハンドラーはリスト 10 のようになります。

リスト 10. Personal Info フォームの JavaScript サブミット・ハンドラー
<script type="text/javascript">
// bind the form and provide a callback function
$('#pform').submit(function() { 

	//submit the form via ajax
	$(this).ajaxSubmit({ 
		target:     '#shippingDetails', 
		url:        'checkout2-fragment.html', 
		success:    function() { 
			var tabSet = $('ul.navTabs');
			tabSet.tabs("enable", 1);
			tabSet.tabs("select", 1);
		}
	});

	//don't actually submit the form normally
	return false; 
});
</script>

このパターンは残りのフラグメント・ファイルのほとんどに適用できます。ただ 1 つの例外は、ステップ 4 の「注文内容の確認 (Order Review )」に対応するファイル (checkout4-fragment.html) です。このファイルには Ajax フォームは必要ありません。前に説明したように、ステップ 5 は Confirmation (確認) ページで、このページはタブ・インターフェースを終了した後の、まったく新しいページとしてロードされます。したがって、ステップ 4 には通常のフォーム・アクションが適しているわけですが、Order Review (注文内容の確認) ページには値引きコードを入力するための迂回路があります。そのため、このフラグメントには、現行のタブに checkout4b-fragment.html の値引きフォームをリロードするための click ハンドラーが必要となります。最終的なハンドラーをリスト 11 に記載します。

リスト 11. Order Review のクリック・ハンドラー
<script type="text/javascript">
$('#enterDiscount').click(function() {
	/*
	grab the url of the current link and load
	the fragment version it in an existing tab
	*/
	$('#orderReview')
		.load(
			$(this).attr("href")
				.replace(".html", "-fragment.html")
			)
		;
	return false;
});
</script>

改良結果の確認

改良のための作業は以上で終わりです。今まで行った変更によって、Customize Me Now 1.2 はバージョン 2.2 に変身しました。

理想のパスを辿った場合

タブ付き購入プロセスが実際にどのように動作するかを確認するため、ブラウザーでこのサイトにアクセスして、ログインや値引きの適用による迂回をせずに、ステップをナビゲートしてください。単一画面のインターフェースが表示され、プロセスの各ステップはそれぞれに固有のタブ内で実行されます。さらに、ステップ間を遷移すると、遷移したことがウィンドウの陰影によるビジュアル・エフェクトによって示されます。最後から 2 番目のステップまで辿り着いて purchase (購入) ボタンをクリックすると、Customize Me Now 1.2 で表示されたような Confirmation ページが表示されます。

複雑なパスを辿った場合

購入プロセスをさらに何度か辿って、ログイン・プロセスや値引きプロセスに迂回してみてください。タブによる直線的なプロセスであることには変わりありませんが、いくつかのステップ (1 および 4) は、複数のサブステップが含まれる複合ステップです。個々のサブステップは視覚的に変化することなくロードされ、次のタブに進むときにはお馴染みのウィンドウの陰影エフェクトが現れます。

ここで創意工夫を働かせて、ステップ 4 までプロセスを完了したら、前のステップをやり直そうとするユーザーをシミュレートしてステップ 2 のタブをクリックしてください。2 番目のタブはフォーム・フィールドに以前入力した値がそのまま入力された状態で再び有効になる一方、3 番目と 4 番目のタブは無効になります。プロセスを逆方向に進んだ場合、フォームを再サブミットしない限りその先には進めません。つまり、リスト 8 に追加したタブを有効あるいは無効にするコードが完全に機能しているということです。

この制約はサンプル・アプリケーションに無くてもよいように思えるかもしれませんが、実際には重要な制約です。ステップ 2 でユーザーが入力した内容によって、ステップ 4 で表示される質問事項は変わってきます。そのため、前の入力内容が変更された場合には、ユーザーはその後のステップをやり直さなければなりません。こういった類の実際のシナリオでは、タブへの再アクセスが行われるたびに、そのタブのコンテンツを新しい Ajax 呼び出しによってリロードするためのコードをタブのインターフェースに追加する必要が出てくるはずです。

Progressive Enhancement をテストする

最後の確認作業として、ブラウザーの設定またはプラグインを使って JavaScript 機能を無効にします。その上で Checkout (購入) ページをリロードし、JavaScript 機能が有効になっていないブラウザーでも、Ajax による改良前のインターフェースを利用して購入プロセスができることを確認してください。今回の手法でも、古いインターフェースを JavaScript 機能に依存するように作成し直すのではなく、Progressive Enhancement の原則に従って古いインターフェースを漸進的に拡張しました。それによって、多種多様なユーザー・エージェントがこれから何年先になっても確実にアプリケーションにアクセスできるようになっています。

これまでの成果

一連のフォームを単一のタグ付きページとしてレンダリングするという単純な目標のわりには、かなりの作業を行いました。それによってもたらされたメリットは何でしょう。その答えはいつもと同じく、ユーザー・エクスペリエンスです。

複数のページにインターフェースが分散された Customize Me Now 1.1 でも問題はありませんでしたが、連続したフォームをロードするプロセスには時間がかかり、ステップ間の遷移でユーザーを混乱させてしまいました。昔ながらのパンくずリストによるナビゲーションは提供されていましたが、視覚的には、パンくずリストが購入プロセスのエクスペリエンスに統一感をもたらすことはありません。パンくずリストのリンクはグローバル・ナビゲーションやテキストによるコンテンツとほとんど見掛けが変わらないため、視覚的に特徴のあるタブ・インターフェースに比べると見落とされがちです。さらに、タブ・インターフェースはまとまりのない一連のページを単一のインターフェースに変えることによって、プロセスに視覚的なつながりがもたらされます。その上、ステップ間で現れるウィンドウの陰影エフェクトがこの統一感を強めるとともに、ユーザーにそれとなくプロセスの進行状態を意識させることになります。

この改良では、ログインや値引きのために迂回するユーザーのエクスペリエンスも多少改善されています。前のインターフェースでは、ユーザーはプロセスのどの段階にいるのか混乱しがちでした。そこで、新しい購入プロセスでは各ステップを構成する複数のアクションをページから消えることのない単一のタブに配置することによって、ユーザーが脇道にそれないようにしています。1 つのタブ内でのサブステップ間では、ウィンドウの陰影エフェクトはないため、ユーザーは現在のステップがまだ完了していないことがわかります。

ここから先の可能性

タブの作成は完了したので、ここからはインターフェースに存在する問題領域の改良を進めることができます。以下は、考えられる拡張のほんの一部です。

  • Ajax の失敗への対処: このサンプル・アプリケーションでのサブミット・ハンドラーは、Ajax 呼び出しが成功した場合しか考慮していません。実稼働レベルのサイトでは、サーバー・エラーやネットワーク遅延による Ajax 呼び出しの失敗を予測し、失敗から回復しなければなりません。
  • フォーム検証の提供: 入力の検証は、開発者とユーザーの両方にとって、大きな頭痛の種の 1 つです。これまでのところ、この厄介な問題は完全に棚上げしていましたが、Ajax フォームの検証を正しく行えば、ユーザー・エクスペリエンスが大幅に向上し、コードの効率性が上がるとともに管理もしやすくなります。
  • ショッピング・カートの常時表示: 新しい Ajax インターフェースは、プロセスが開始する時点と終了する時点にしか、ユーザーに各自のショッピング・カートを表示しません。タブの隣のサイドバーに簡易化したカートのビューを追加して、プロセスの各ステップが完了するごとに更新するのも一案です。このようにして注文内容を常にフィードバックすることは、ユーザーにとって大きな価値があります。
  • 「戻る」ボタンの管理の組み込み: 改良には成功したものの、これによってユーザーには新たな問題が生じています。それは、購入プロセス以外では「戻る」ボタンが使えないことです。ただし、この問題は、Ajax 履歴管理ライブラリーを使用すれば簡単に修正することができます。このライブラリーでは、新しいフォームがタブにロードされるたびにブラウザーの履歴スタックにエントリーを追加することができます。このようにすれば、ユーザーが「戻る」ボタンをクリックしたときに、ユーザーは購入プロセスから引きずり出されて前にいたページに戻されるのではなく、ユーザーには前のタブが表示されることになります。この方法で問題を修正するには、jQuery、Prototype、そしてその他多くのAjax フレームワークと連動する Really Simple History といったスタンドアロンのライブラリーに目を向けてください (実は、この記事の著者は Really Simple History のコード管理人です)。

このように可能性にはきりがありませんが、購入プロセスの改良を続けるにあたって、この記事で行った変更が強力な Ajax のバックボーンとなることは確かです。


ダウンロード

内容ファイル名サイズ
Source code for the original demo appwa-aj-overhaul4OnePointTwo.zip832KB
Source code for the retrofitted demo appwa-aj-overhaul4TwoPointTwo.zip928KB

参考文献

学ぶために

  • jQuery マニュアルの Web サイトで jQuery アプリケーション・プログラム・インターフェース (API) について調べてください。
  • Learning jQuery Web サイトから jQuery コミュニティーに加わって、チュートリアルやフォーラムにアクセスしてください。
  • jQuery を使って Ajax 開発を単純化する」(Jesse Skinner 著、developerWorks、2007年4月) などの他の記事を読んで、Ajax についてさらに学んでください。
  • jQuery in Action』(Manning Publication Co.、2008年2月) も jQuery の手引書となります。
  • jQuery とその他の UI については、Brian Dillard のブログ、Agile Ajax を調べてください。
  • Billy Hoffman と Bryan Sullivan の共著による『Ajax Security』(Addison Wesley Professional、2007年12月) では、Ajax アプリケーションのセキュリティーに関するベスト・プラクティスを包括的に概説しています。
  • 有名な Web 開発者、Luke Wroblewski のブログ、Functioning Form にアクセスしてください。Web フォーム設計のベスト・プラクティスについて継続的に解説しています。
  • technology bookstore で、この記事で取り上げた技術やその他の技術に関する本を探してください。

製品や技術を入手するために

  • jQuery のメイン Web サイトで、jQuery をダウンロードして、その原理について学んでください。このサイトには、追加プラグインも用意されています。この記事を書いている時点での jQuery の最新バージョンは 1.2.3 です。
  • jQuery プラグインの 1 つ、jQuery UI Tabs では、インラインまたは Ajax コンテンツをタブ付きインターフェースにラップすることができます。このプラグインは、カスタマイズ可能なウィジェットとユーザー・インターフェース・コンポーネントをセットにした jQuery UI コレクションに含まれています。
  • jQuery プラグインの 1 つ、jQuery Form では、多彩な直観的で便利なメソッドを使って、HTML フォームを強力な Ajax コンポーネントに変換できます。
  • Really Simple History について調べてください。この Ajax 履歴ライブラリーは、ユーザーが Web アプリケーションに期待する「戻る」ボタンとブックマークの振る舞いをリストアします。

議論するために

その他のダウンロード

  • デモ: Customize Me Now 1.21 (著者の会社の Web サイトで、元のデモ・アプリケーションの動作を確認してください。)
  • デモ: Customize Me Now 2.22 (著者の会社の Web サイトで、改良後のデモ・アプリケーションの動作を確認してください。)
  • デモ: Customize Me Now: すべてのバージョン3 (著者の会社の Web サイトで、これまでの記事で作成したバージョンを含め、すべてのバージョンのデモ・アプリケーションの動作を確認してください。)

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development, XML
ArticleID=334235
ArticleTitle=Ajax による改良: 第 4 回 jQuery および Ajax フォームで既存のサイトを改良する
publish-date=07292008