Ajax による改良: 第 3 回 jQuery、Ajax タブ、そしてフォト・カルーセルで既存のサイトを改良する

オープンソースのフレームワークとプラグインによるインターフェースの最新化、そしてユーザー帯域幅の節約

Ajax の手法は大規模な商用 Web アプリケーションの様相を一変させましたが、規模の小さな多くの Web サイトにはユーザー・インターフェース (UI) をまるごと一晩にしてリビルドするだけのリソースはありません。しかし Ajax による新しい機能を利用すると、実際のインターフェースの問題が解決され、ユーザー・エクスペリエンスも改善されることで、新しい機能にかかるコストが妥当なものであることが証明されるはずです。この連載では、オープンソースによるクライアント・サイドのライブラリーを使ってユーザー・インターフェースを徐々に最新のものにする方法を説明しています。今回の記事で説明するのは、表示に時間のかかる煩雑で厄介な製品詳細ページを、DHTML と Ajax によって高速で洗練されたページに変身させる方法です。しかも、Progressive Enhancement (漸進的な機能拡張) の原則に従うことで、あらゆるユーザー・エージェントがサイトにアクセスしたままで変身できることを確実にします。

Brian J. Dillard (bdillard@pathf.com), RIA Evangelist, 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月 08日

この記事について

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

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


第 1 回と第 2 回の復習

この連載の第 1 回第 2 回ではサンプル・アプリケーション、Customize Me Now について紹介し、このアプリケーションを Web 1.0 バージョンから Ajax で強化した Web 2.0 バージョンに改良するプロセスを開始しました。改良するにあたって、ビジネス上の理由とユーザビリティーからの理由を説明しています。また、オープンソースのツールとして JavaScript のフレームワークである jQuery、そして jQuery のプラグインをセットアップする方法も説明しました。これらのライブラリーを使って、ポップアップ、オフサイト・リンク、ナビゲーションの脇道をモーダル・ダイアログ、ツールチップ、ライトボックスに置き換えることで、Customize Me Now のユーザー・フローを簡素化しました。これらの改良は Progressive Enhancement の原則に従っているため、JavaScript が無効にされると、改良後の Web 2.0 アプリケーションを駆動するページがそのまま自動的に Web 1.0 のエクスペリエンスに戻るようになっています。

第 3 回での目標

この記事では、扱いにくい製品詳細ページを簡単に制御できるようにします。その手法としては、まずコンテンツをタブ付きインターフェースの内部に配置します。さらに、製品の画像は画像カルーセルに表示することで制御します。この 2 つの手法を、単純な DHTML (Dynamic HTML)、あるいはそれよりも複雑な Ajax コードを使用して採り入れる方法を学んでください。DHTML と Ajax のどちらを使うにせよ、今回も同じく Progressive Enhancement の原則に従って、JavaScript が無効にされた場合でもページにアクセスできるようにします。今回の改良を達成するために、さらに 2 つの jQuery プラグインを追加します。1 つは画像スライドショー用の jCarousel、そしてもう 1 つはタブ用の jQuery UI Tabs です。

この記事のコンセプトを理解するには、Customize Me Now 1.1 のサイトを参照してください。このサイトは、オリジナルのサンプル・サイトを多少変更したバージョンで、Ajax はまだ使用されていません。この 1.1 に変更を加えながら、連載全体を通して説明するすべての変更を組み込んだ Customize Me Now 2.1 を作成します。

2 つのタイプの製品詳細: 単一ページとマルチページ

どの e-コマース Web サイトでも、とりわけ複雑なセクションは製品詳細の部分です。これらのサイトには、単純な製品説明や技術仕様から、ユーザー・レビューなどのコミュニティーが生成するコンテンツに至るまで、製品に関する大量の情報が蓄積されるからです。さらには当然、製品の画像もあり、おびただしい数に上る製品ごとの写真があることも珍しくありません。ユーザー・エクスペリエンスの点で課題となるのは、製品の購入を決定する上で十分なデータをカスタマーに提供すると同時に、あまりにも多くのデータでカスタマーを情報攻めにしないようにすることです。

いくつかの点で、Customize Me Now 1.0 は Ajax で改良しやすいものでした。このサイトが提示する製品詳細のコンテンツは、単一のページに簡単に収まるからです。そこで、Customize Me Now 2.0 では jQuery と Thickbox を使って元のページをモーダル・ダイアログに置き換えることによって、ユーザーが検索して購入するまでの「理想のパス」を簡素化しました。

しかし今回、必要条件は変わっています。Customize Me Now 1.1 は、1.0 より遥かに多くの詳細な製品情報を表示し、そのコンテンツには多数の長々としたテキスト・ブロックと多数の大きな写真が含まれます (これらの画像は、人気の高い Web 2.0 写真共有ネットワーク、Flickr から引用したものです)。Ajax が登場する以前は、このような大量のコンテンツを表示するには 2 つの方法が採られていました。1 つはスクロール可能な 1 つの長いページに表示するという方法 (図 1図 2)、もう 1 つはテキスト・ブロックや写真ごとに複数のページに分割して表示するという方法です (図 3図 4)。

Web ブラウザーで Customize Me Now 1.1 にアクセスすると、Product Details (製品詳細) ページのバージョン A とバージョン B を Customize Me Now 1.0 の古いバージョンと比較することができます。グローバル・ヘッダーとフッターには、この 3 つすべてのバージョンへのリンク (A、B、old) が表示されます。以下の図を見るとわかるように、バージョン A とバージョン B のユーザビリティーには、古いバージョンに比べ、かなり大きな問題がありそうです。これらの新しいバージョンの Product Details ページには、明らかに Thickbox モーダル・ダイアログでは対応しきれません。

図 1. バージョン A の Product Details ページ: 単一ページのテキスト・コンテンツ
バージョン A の Product Details ページ: 単一ページのテキスト・コンテンツ
図 2. バージョン A の Product Details ページ: 単一ページの画像コンテンツ
バージョン A の Product Details ページ: 単一ページの画像コンテンツ

バージョン A は、ユーザーだけでなく、ブラウザーとサーバーをも情報過多の危険にさらします。ユーザーはこの表示の下に続くコンテンツに気付くと莫大な量の情報に圧倒され、その一方、ブラウザーとサーバーは送信されてくる大量のデータに四苦八苦します。写真が 6 枚だけのページなら、ブロードバンドの速度ではかなり短時間でロードされますが、写真が 16 枚、あるいは 60 枚もあったとしたらどうなるでしょう。さらにユーザー・レビューが 150 件もある場合や、ユーザーが速度の遅い接続を使用している場合のことを考えてみてください。また、製品に関するあらゆるデータが一度にロードされると、最終的にはパフォーマンスに影響が出るだけでなく、消化しきれないほどのコンテンツにユーザーまでもが苦しむことになります。

図 3. バージョン B の Product Details ページ: マルチページのテキスト・コンテンツ
バージョン B の Product Details ページ: マルチページのテキスト・コンテンツ
図 4. バージョン B の Product Details ページ: マルチページの画像コンテンツ
バージョン B の Product Details ページ: マルチページの画像コンテンツ

バージョン B では、各ページに情報を少しずつ表示することによって、ユーザーを圧倒させないようにしています。しかし、ユーザーは詳細情報を表示しようとクリックするたびに、新しいページがロードされるまで待たなければなりません。その上、バージョン B のサブページのそれぞれには、訳がわからなくなるほど大量のリンクが並ぶことになります。データは過多ではないかもしれませんが、ナビゲーションが圧倒的な多さであることは確かです。

単一ページのバージョンを改良する方法

バージョン A はすべてが 1 つのページ上にあるため、画像スライドショーとタブ付きインターフェースを使って改良するのは至って簡単です。Ajax は一切必要ありません。必要なのは昔ながらの DHTML だけで、この手法には Progressive Enhancement が含まれているという利点があります。ブラウザーに提供するのは相変わらず長々としたスクロール・ページのコンテンツですが、JavaScript コードがこれを最新の手法による使いやすいインターフェースに変換します。一方、JavaScript 対応のブラウザーを使用していないユーザーには、元のページが表示されます。もちろんこの手法では、単一ページで表示するという方法に伴う帯域幅の問題に対しては、何の対策も採っていません。

オープンソースのツールをダウンロードしてインストールする

Ajax による改良を始めるには、まず、新しい機能すべての土台となる jQuery の最新バージョンをダウンロードします (「参考文献」を参照)。この連載の第 1 回と第 2 回の手順に従っていれば、jQuery 1.2.1 がすでにインストールされているはずです。この記事を書いている時点での最新バージョンは 1.2.3 で、このバージョンにはいくつかのバグ・フィックスが追加されています。

さらに、「参考文献」セクションに記載されている 2 つのプラグインもダウンロードする必要があります。そのうちの 1 つ、jQuery UI Tabs は jQuery UI の一部で、構成可能なユーザー・インターフェース・ウィジェットとコンポーネントがセットになったプラグインです。jQuery UI Tabs は ul 要素をタブ付きインターフェースに変換し、インラインのチャンクや Ajax マークアップをタブのコンテンツに変換します。もう一方の jCarousel は、画像のセットをスライドショーに変換するスタンドアロンのプラグインです。jQuery UI のタブの場合と同じく、これらのスライドショーのコンテンツは、インラインにすることも、Ajax にすることもできます。

必要なコンポーネントをダウンロードしたら、アプリケーションでの適切なディレクトリー構造に配置してください。各ダウンロードにはコード・サンプルや関係のないファイルが含まれています。これらについては各自の判断で保存または削除することができます。本番用にはパッケージ・バージョンまたは縮小バージョンの JavaScript ライブラリーが適していますが、完全なソース・コードを保持しておけば、そのコードを読んで各コンポーネントについての理解を深めることができます。

  • jQuery で保持しておかなければならない唯一のファイルは、ライブラリー自体の Minified (縮小) 版です。
  • jQuery UI Tabs では、ライブラリーの Packed (圧縮) 版と付属 CSS ファイル、そして loading.gif と tab.png という 2 つの画像を保持する必要があります。tab.png はサイトの背景色が白であることを前提しているので、この画像の角丸が Customize Me Now の実際の背景色と合うように、Adobe Photoshop などの画像編集プログラムで修正しても構いません。
  • jCarousel については、 Packed (圧縮) 版のライブラリーとこれに付属の CSS ファイルに加え、ライブラリーと一緒に提供される事前パッケージ化された 3 つのスキンいずれかのディレクトリー全体を保持する必要があります。jCarousel で言うスキンとは CSS ファイルと、カルーセルの表示スタイルを制御するための画像セットのことです。Customize Me Now には、Tango スキンを保持しますが、ディレクトリーは「tango-modified」という名前に変更してください。この後、デフォルトの Tango テーマにいくつか変更を加えるためです。

ファイルを所定の位置に配置したら、これらのファイルを detailA.html の head に追加します。リスト 1 に、その結果を示します。

リスト 1. jQuery とそのプラグインのデプロイメント
<!--jquery assets-->
<script type="text/javascript"
	src="../js/jquery-1.2.3.minjs"></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">

<!--jcarousel assets-->
<script type="text/javascript"
	src="../jcarousel/lib/jquery.jcarousel.pack.js"></script>
<link rel="stylesheet" type="text/css"
	href="../jcarousel/lib/jquery.jcarousel.css" />
<link rel="stylesheet" type="text/css"
	href="../jcarousel/skins/tango-modified/skin.css" />

DHTML 画像カルーセルを作成する

DHTML 画像カルーセルを作成するには、まず jCarousel のルック・アンド・フィールを変更します。jCarousel は画像カルーセルを横に表示するようにも、縦に表示するようにも構成できるので、そのスタイルシートには両方のタイプのルールが含まれています。このサンプルではスライドショーは横向きに表示するので、縦のスライドショーに関するすべての CSS 宣言は削除して構いません。さらに、さまざまな要素の幅、高さ、パディング、マージンも変更します。jCarousel はデフォルトで、常に 3 つのサムネール画像が表示されるスライドショーを作成します。このサンプルのスライドショーでは、画像がかなりの大きさなので (それぞれ幅 500 ピクセル)、一度に 1 つの画像だけを表示することにします。変更を加えた後の skin.css は、リスト 2 のようになります。

リスト 2. スライドショーの CSS コード
..jcarousel-skin-tango.jcarousel-container {
    -moz-border-radius: 10px;
    background: #F0F6F9;
    border: 1px solid #346F97;
}

..jcarousel-skin-tango.jcarousel-container-horizontal {
    width: 502px;
    padding: 20px 125px !important;
}

.jcarousel-skin-tango .jcarousel-clip-horizontal {
    width:  502px;
    height: 410px;
}

..jcarousel-skin-tango .jcarousel-item {
    width: 502px;
    height: 410px;
}

..jcarousel-skin-tango .jcarousel-item-horizontal {
    margin-right: 125px;
}

..jcarousel-skin-tango .jcarousel-item-placeholder {
    background: #fff;
    color: #000;
}

/**
 *  Horizontal Buttons
 */
..jcarousel-skin-tango .jcarousel-next-horizontal {
    position: absolute;
    top: 43px;
    right: 5px;
    width: 32px;
    height: 32px;
    cursor: pointer;
    background: transparent url(next-horizontal.png) no-repeat 0 0;
}

..jcarousel-skin-tango .jcarousel-next-horizontal:hover {
    background-position: -32px 0;
}

..jcarousel-skin-tango .jcarousel-next-horizontal:active {
    background-position: -64px 0;
}

..jcarousel-skin-tango .jcarousel-next-disabled-horizontal,
..jcarousel-skin-tango .jcarousel-next-disabled-horizontal:hover,
..jcarousel-skin-tango .jcarousel-next-disabled-horizontal:active {
    cursor: default;
    background-position: -96px 0;
}

..jcarousel-skin-tango .jcarousel-prev-horizontal {
    position: absolute;
    top: 43px;
    left: 5px;
    width: 32px;
    height: 32px;
    cursor: pointer;
    background: transparent url(prev-horizontal.png) no-repeat 0 0;
}

..jcarousel-skin-tango .jcarousel-prev-horizontal:hover {
    background-position: -32px 0;
}

..jcarousel-skin-tango .jcarousel-prev-horizontal:active {
    background-position: -64px 0;
}

..jcarousel-skin-tango .jcarousel-prev-disabled-horizontal,
..jcarousel-skin-tango .jcarousel-prev-disabled-horizontal:hover,
..jcarousel-skin-tango .jcarousel-prev-disabled-horizontal:active {
    cursor: default;
    background-position: -96px 0;
}

次は、detailAhtml に 1 つのちょっとした変更を加えます。ページの元のバージョンでは、画像はすでに ul 要素として構成されているので、この HTML リストにラッパー div を追加します。ラッパー divjcarousel-skin-tangoclass 属性に追加することで、変更した Tango スキンのスタイル・ルールがカルーセルに適用されるようにするというわけです。また、div には imageCarouselid 属性を指定し、jQuery が DOM (Document Object Model) を解析して変換対象の正しい要素を検出できるようにしなければなりません。この作業が完了すると、HTML はリスト 3 のようになります。

リスト 3. スライドショーの HTML コード
<div id="imageCarousel" class="jcarousel-skin-tango">
	<ul class="productImages">
		<li>
			<img class="product" alt="product photo"
				width="500" height="375" src="../img/pizza1.jpg" />
			Photo credit: <a target="_blank"
			href="http://www.flickr.com/photos/kankan/">Kanko*</a>,
			Flickr, Creative Commons Attribution License
		</li>
		<!--additional <li> items, images and photo credits here-->
	</ul>
</div>

最後に、スライドショーをビルドする JavaScript コードを作成します。jCarousel の芸当のおかげで、このコードはわずか数行しかありません。HTML 文書の head に組み込んだ JavaScript ファイルと CSS ファイルの下に、リスト 4 に記載するインライン・スクリプト・ブロックを追加してください。

リスト 4. スライドショーの JavaScript コード
<script type="text/javascript">
$(document).ready(function() {
 
	$('#imageCarousel').jcarousel({
		scroll: 1
	});
 
});
</script>

この JavaScript コードは単純に見えますが、実は負荷の重いすべての仕事を引き受けます。このコードは jQuery の ready イベント・ハンドラーを呼び出して、ページの DOM がロードされてから追加タスクを実行するように jQuery に指示します。次に jQuery のセレクター・メカニズムである $ 関数を呼び出し、jQuery に id 属性が imageCarousel に設定された DOM 要素を見つけさせます。そしてこの DOM ノードが含まれるオブジェクトで jcarousel メソッドを呼び出すことで、該当するノード内のすべてのマークアップを画像スライドショーに変換するように jCarousel 指示します。jcarousel メソッドに渡している 1 つの引数は、構成パラメーターのハッシュです。大抵の jCarousel のデフォルト・オプションはそのまま使えるので、このハッシュにはキーと値のペアが 1 つしか含まれません。キーが scroll、値が 1 のこのペアが、jCarousel に一度に 1 つの画像だけを表示するように指示します。

これで、イメージ・スライドショーは完成です。

DHTML タブを作成する

ここからは、ページ上のコンテンツのセクションごとに DHTML タグを作成する作業に移ります。まず始めに、コンテンツの個々のセクションを、固有の id 属性と tabContent の class 属性を設定した新しい div でラップしてください。この作業によって、コンテンツの各セクションはリスト 5 のようになります。これらのラッパーの目的は、jQuery UI Tabs にフックを提供して、タブ付きコンテンツに変換するマークアップのチャンクを認識できるようにすることです。

リスト 5. detailA.html のコンテンツを構成する 5 つのセクションすべてに対応する HTML
<div class="tabContent" id="introduction">

	<h2>Introduction</h2>

	<!--paragraphs of text content here-->
 
</div>

<div class="tabContent" id="moreDetails">

	<h2>More Details</h2>

	<!--paragraphs of text content here-->

</div>

<div class="tabContent" id="userReviews">

	<h2>User Reviews</h2>

	<!--paragraphs of text content here-->

</div>

<div class="tabContent" id="techSpecs">

	<h2>Technical Specifications</h2>

	<!--paragraphs of text content here-->

</div>

<div class="tabContent" id="productImages">

	<h2>Product Images</h2>

	<div id="imageCarousel" class="jcarousel-skin-tango">
    	<ul class="productImages">
			<!--<li> items from your image slideshow here-->
		</ul>
	</div>

</div>

次に、HTML 文書にマークアップを追加します。これはリンクの順序なしリストで、文書の先頭のほうの、実際のコンテンツの最初のセクションの直前に配置されます。jQuery UI Tabs はこのマークアップをコンテンツへの新しいタブ・インターフェースに変換します。リンクの href 属性は内部文書のアンカーのように見えますが、実際には先ほど作成したラッパー divid 属性に対応します。リスト 6 に、この追加マークアップを記載します。

リスト 6. detailA.html の追加 HTML コンテンツ
<ul class="navTabs">
    <li><a href="#introduction"><span>Introduction</span></a></li>
    <li><a href="#moreDetails"><span>More Details</span></a></li>
    <li><a href="#userReviews"><span>User Reviews</span></a></li>
    <li><a href="#techSpecs"><span>Technical Specifications</span></a></li>
    <li><a href="#productImages"><span>Product Images</span></a></li>
</ul>

続いて 2 つのスタイル・セットを作成します。それぞれ、JavaScript が有効にされているユーザーとそうでないユーザーを対象としたセットです。JavaScript が有効にされているユーザーには新しいタブ付きインターフェースが表示されるので、タブ付きコンテンツの外観を整える必要があります。そこで、まずはパディングとボーダーを追加し、次に各タブ付きセクション内部に h2 要素が表示されないようにします。これらの見出しはタブ上のテキスト・ラベルと同じ内容を繰り返すことになるため、新しいインターフェースでは冗長になってしまうからです。これらのスタイル・ルールを作成するには、グローバル・スタイルシートにルールを追加します。

JavaScript が無効にされているユーザーには、タブ付きインターフェースは表示されません。これらのユーザーには、文書に追加した順序なしリストを非表示にし、コンテンツ・セクションに追加したボーダーとパディングを削除し、さらに h2 要素が表示されるように戻す必要があります。幸い、これらの要件には noscript タグで簡単に対応できます。noscript タグ内部に CSS ルールを追加すれば、JavaScript が無効にされたブラウザーでのグローバル・スタイルは無効になり、追加した CSS ルールのスタイルが適用されるようになります。ただし、これらのスタイルが適用されるのは、JavaScript がオフになっている場合のみです。

最終的に、ブラウザーの機能に応じた (機能によってかなり異なる適切な) マークアップのセットが出来上がります。この作業が終わると、2 つの CSS ブロックはそれぞれリスト 7、リスト 8 のようになっているはずです。

リスト 7. customizemenow.css の追加 CSS スタイル
#CMN .tabContent {
	padding: 14px;
	border: 1px solid #97a5b0;
}
#CMN .tabContent h2{
	display: none;
}
リスト 8. detailA.html の noscript ブロックに含まれる追加スタイル
<noscript>
	<style type="text/css">
		#CMN .tabContent {
			padding: 0;
			border: 0;
		}
		#CMN .tabContent h2 {
			display: block;
		}
		#CMN ul.nav {
			display: none;
		}
	</style>
</noscript>

最後に追加するのは、DHTML タブを作成するカスタム JavaScript です。そのためには、前に作成したインライン・スクリプト・ブロック (リスト 4 を参照) にさらにコードを追加します。リスト 9 は、コードを追加した後のインライン・スクリプト・ブロックです。

リスト 9. detailA.html のカスタム JavaScript
$(document).ready(function() {
 
	/*earlier jCarousel code goes first*/

	/*create tabs from an unordered list using jquery.ui.tabs*/
	$('ul.navTabs').tabs(
		{ fx: { height: 'toggle', opacity: 'toggle' } }
	);
 
});

jCarousel と同様に、jQuery UI Tabs のカスタム JavaScript は予想に反して単純に見えます。ここでも DOM 要素を見つけるために使用するのは jQuery のセレクター・メカニズムですが、この場合の検索対象はリンクの新しい順序なしリストです。続いて jQuery UI Tabs の tabs メソッドが該当する ul 要素とその li 子要素をタブ付きインターフェースに変換します。tabs メソッドは、順序なしリスト内のリンクを調べ、各タブをコンテンツの対応する div 全体と突き合わせます。各 div は対応する li 要素をユーザーがクリックするまで非表示にされ、この要素がクリックされた時点でタブとしてスタイル設定されます。jcarousel メソッドの場合と同じく、初期化パラメーターのハッシュを使って tabs メソッドの振る舞いをカスタマイズすることができます。このサンプルではビジュアル・エフェクトを加え、ブラウザーでタブがスマートに起動されるように見せます。最後に注意しておく点として、jCarousel 画像スライドショーの作成は、タブ・インターフェースを作成する前に行わなければなりません。タブ・インターフェースを最初に作成すると、jCarousel が画像カルーセル・マークアップの CSS プロパティーで混乱してしまうため、問題が発生します。

単一ページ・バージョンを再検討する

Customize Me Now 2.1 のバージョン A の Product Details ページを見てみると、DHTML タブ (図 5) と画像カルーセル (図 6) が動作している様子がわかります。フェードアウトやウィンドウの陰影などのエフェクトを確認するには、このページを Web ブラウザーにロードしてください。

図 5. 変更後の Product Details ページ: タブ付きコンテンツ
変更後の Product Details ページ: タブ付きコンテンツ
図 6. 変更後の Product Details ページ: 画像カルーセル
変更後の Product Details ページ: 画像カルーセル

バージョン A の Product Details 2.1 には、同じページのバージョン 1.1 に比べ、いくつか優れている点があります。まず、情報の複雑さと量をユーザーに見せないようにすることによって、1.1 のような圧倒されるインターフェースではなくなりました。また、タブでナビゲーションするメタフォーでは、ユーザーがバイト・サイズで情報を消化することができます。さらにブラウザーで JavaScript を無効にしてページをリロードしてみてください。すると、バージョン 1.1 とほとんど同じページが表示されます。これこそが、Progressive Enhancement が発揮する威力です。ただし残念ながら、このバージョンは単一ページの手法に伴う帯域幅の問題についてはほとんど解決していません。ユーザーはやはり、一度も見ることがないとしても大量のテキストと画像をダウンロードすることになります。次のセクションでは、この問題に取り組みます。

マルチページ・バージョンを改良する方法

単一ページの Product Details ページにはタブと画像カルーセルを追加したので、今度はマルチページ・バージョンで同様の変換を行う番です。今回は、Ajax を使ってタブとカルーセルのコンテンツを動的にページに組み込むため、コードはさらに複雑になります。この作業の報いとなるのは、ページのロード時間が短縮されること、そして帯域幅が節約されることです。追加のコンテンツは動的にロードされますが、この動的ロードは必要なときにしか行われません。

Ajax 画像カルーセルを作成する

マルチページの Product Details セクションから画像カルーセルを作成するための最初のステップとして、新しい JSON (JavaScript Object Notation) 文書を作成します。JSON は XML に似たデータ転送フォーマットですが、XML よりも軽量です。JSON が Ajax に最適なわけは、jQuery やその他の Ajax フレームワークでは JSON 文書を安全に、しかもほとんど即時に JavaScript オブジェクトに変換できるためです。ここで作成する JSON 文書にはマークアップは含まれません。JavaScript コードによってスライドショーに変換される画像の URL とメタデータがあるだけです。この文書を作成したら、detailB5-fragment.html という名前を付けて保存してください (リスト 10 を参照)。

リスト 10. detailB5-fragment.html から抜粋した JSON データ
{"items": [
	{
		"url": "pizza1.jpg",
		"width": "500",
		"height": "375",
		"creditURL": "kankan",
		"creditLabel": "Kanko*"
	},
	{
		"url": "pizza2.jpg",
		"width": "500",
		"height": "374",
		"creditURL": "lenore-m",
		"creditLabel": "L. Marie"
	},
	{
		"url": "pizza3.jpg",
		"width": "500",
		"height": "375",
		"creditURL": "roadhunter",
		"creditLabel": "Topato"
	},
	{
		"url": "pizza4.jpg",
		"width": "500",
		"height": "369",
		"creditURL": "sgt_spanky",
		"creditLabel": "Kevitivity"
	},
	{
		"url": "pizza5.jpg",
		"width": "500",
		"height": "368",
		"creditURL": "fooey",
		"creditLabel": "foéöÞoooey"
	},
	{
		"url": "pizza6.jpg",
		"width": "500",
		"height": "334",
		"creditURL": "pancakejess",
		"creditLabel": "jsLander"
	}
]}

次に、前回 detailA.html で行ったように (リスト 1 を参照)、jQuery、jQuery UI Tabs、jCarousel のすべてのファイルを detailB1.html のヘッダーに追加します。続いて、detailB1.html の body に HTML を追加して、画像スライドショーのプレースホルダーを作成します。jCarousel はこの空の順序なしリストに画像コンテンツを設定することになります。リスト 11 に、プレースホルダーの HTML を記載します。

リスト 11. detailB1.html から抜粋した JavaScript コード
<div class="tabContent" id="productImages">
 
	<h2>Product Images</h2>
 
	<div id="imageCarousel" class="jcarousel-skin-tango">
    	<ul class="productImages">
		</ul>
	</div>
 
</div>

最後に追加するのは、カスタム JavaScript コードです。このコードは、HTML 文書の head に含まれるファイル直下のスクリプト・ブロックに追加します。このスクリプト・ブロックは、リスト 12 のようになります。

リスト 12. detailB1.html から抜粋した JavaScript コード
<script type="text/javascript">

window.alert = function() {
	return;
};

$(document).ready(function() {
 
	/*
		create an image slideshow from a JS array of URLs using
		jcarousel
	*/
	var itemLoadCallback = function(carousel, state) {
		if (state != 'init') {
			return;
		}
		jQuery.getJSON("detailB5-fragment.html", function(data){
	 		itemAddCallback(carousel, carousel.first,
				carousel.last, data.items);
		});
	};

	var itemAddCallback = function(carousel, first, last, data) {
	    for (i = 0, j = data.length; i < j; i++) {
	        carousel.add(i, getItemHTML(data[i]));
	    }
	    carousel.size(data.length);
	};

	var getItemHTML = function(d) {
		return '<img class="product" alt="product photo" width="' +
			d.width + '" height="' +
			d.height + '" src="../img/' +
			d.url + '" />Photo credit: <a target="_blank"' +
			' href="http://www.flickr.com/photos/' +
			d.creditURL + 's/">' +
			d.creditLabel + '</a>, Flickr, ' +
			'Create Commons Attribution License'
		;
	};

	jQuery('#imageCarousel').jcarousel({
		itemLoadCallback: itemLoadCallback,
		scroll: 1
	});
 
	jQuery('ul.productImages').css("width","3012px");
 
});
</script>

ご覧のように、この JavaScript は今まで作成したコードのどれよりも複雑です。このコードには以下の関数が含まれます。

  • itemLoadCallback。Ajax を使用して、先ほど作成した JSON 文書からデータを読み取り、そのデータを itemAddCallback に渡します。
  • itemAddCallBackitemLoadCallback によってロードされた JSON データを解析し、個々の画像をカルーセルに追加します。
  • getItemHTML。JSON データを jCarousel のマークアップにフォーマット設定して DOM に挿入します。

上記の関数を呼び出すには、itemLoadCallback をオプション・ハッシュの一部として jcarousel メソッドに渡します。これにより、jCarousel はスライドショーを DOM に既に存在するマークアップからビルドするのではなく、Ajax データから動的にビルドしなければならないことを認識し、itemLoadCallback とそのヘルパー関数がそれぞれの役割を果たします。ただし、ここで障害となるのが、jCarousel に伴ういくつかの特異性です。

まず、jCarousel は動的にロードされたコンテンツの CSS プロパティーを誤って計算するようです。その結果、スライドショーではいくつかの画像が表示されません。多少デバッグしてみると、画像が含まれる ul の幅について、jCarousel が混乱していることがわかります。時間をかけてこの問題をデバッグし、これを修正するように jCarousel コードを拡張することも可能ですが、強引な方法のほうが手っ取り早く問題を解決できます。その方法とは、カルーセルがビルドされた後に jQuery の css メソッドを使ってその幅を調整し、バージョン A のProduct Details で非動的に生成されたカルーセルの幅と合わせることです。

2 つ目の jCarousel に関する問題は、最初の問題に関連するもので、jCarousel は DOM 要素の幅を正しく計算しないことから、ブラウザーのウィンドウ・サイズを変更するたびにエラー・メッセージが表示されます。このエラー・メッセージは JavaScript のアラート・ボックスとして表示されるため、必ずしも最適なユーザー・エクスペリエンスとは言えません。この振る舞いを無効にするには、JavaScript の組み込み window.alert メソッドをダミー関数に置き換えてください。これで問題は解決します。

Ajax タブを作成する

jQuery UI Tabs をマルチページの Product Details セクションにデプロイするには、インライン・コンテンツと動的 Ajax コンテンツの両方を使用します。detailB1.html ファイルは、前に detailB2.html、detailB3.html、detailB4.html で組み込んだコンテンツすべてのラッパー・ページとして機能します。これらのファイルのコンテンツを Ajax によって detailB1.html に組み込む際の問題は、それぞれのページに完全な HTML 文書が含まれているという点です。実際に必要なのは、ページごとに 1 つの div に過ぎません。この問題を解決する方法として、それぞれのファイルをコピーして新しいファイル名を指定し、余分なマークアップを取り除いて各ページの代替バージョンを作成します。その結果、detailB2-fragment.html、detailB3-fragment.html、detailB4-fragment.html という 3 つの新しいファイルが作成されます。各ファイルはリスト 13 のような内容です。もちろん実際には、サーバー・サイドのテンプレート・エンジンを使って、このコンテンツを完全な HTML バージョンとフラグメント・バージョンの両方で提供することになります。例えば、Ruby on Rails などのフレームワークはリクエストが通常の呼び出しか、あるいは Ajax 呼び出しかによって、同じコンテンツに自動的に異なるラッパーを適用することができます。しかし、この純粋なクライアント・サイドのサンプル・コードでは、この効果を別々のファイルでシミュレートします。

リスト 13. HTML フラグメント・ファイルから抜粋したマークアップ
<div class="tabContent" id="moreDetails">
 
	<h2>More Details</h2>

	<!--paragraphs of text content here-->

</div>

次に必要なのは、detailB1.html 内にあるマークアップの一部を変更することです。Customize Me Now 1.1では、このページに 2 次ナビゲーション・メニューを組み込んでユーザーがページを移動できるようにしました。この元のマークアップをリスト 14 に記載します。このマークアップにいくつの変更を加えることで、jQuery UI Tabs がタブ・インターフェースに変換できるようにします。変更後のマークアップは、リスト 15 のようになります。

リスト 14. detailB1.html での元の 2 次ナビゲーション用マークアップ
<ul class="nav">
	<li><a href="detailB1.html">Introduction</a></li>
	<li><a href="detailB2.html">More Details</a></li>
	<li><a href="detailB3.html">User Reviews</a></li>
	<li><a href="detailB4.html">Technical Specifications</a></li>
	<li class="last"><a href="detailB5a.html">Photos</a></li>
</ul>
リスト 15. detailB1.html での変更後の Ajax タブ用マークアップ
<ul class="nav">
    <li><a href="detailB1.html"><span>Introduction</span></a></li>
    <li><a href="detailB2.html"><span>More Details</span></a></li>
    <li><a href="detailB3.html"><span>User Reviews</span></a></li>
    <li><a href="detailB4.html"><span>Technical Specifications</span></a></li>
    <li class="last"><a href="detailB5a.html"><span>Product Images</span></a></li>
</ul>

次に、スライドショーのために前にこのページに追加したインライン・スクリプト・ブロックに JavaScript コードを追加します。追加後のスクリプト・ブロックはリスト 16 のようになります。

リスト 16. Ajax タブを作成するためのインライン・スクリプト・ブロック
$(document).ready(function() {
 
	/*transform urls for tabs with inline content*/
	$('ul.nav > li:first > a').attr("href", "#introduction");
	$('ul.nav > li:last > a').attr("href", "#productImages");

	/*transform urls for tabs with ajax content*/
	$('ul.nav > li:not(:first):not(:last) > a').each(function (i) {
		var el = $(this);
		el.attr("href", el.attr("href").replace(".html",
			"-fragment.html"));
	});
 
	/*earlier jCarousel code goes here*/
 
	/*
		replace ul classname of "nav" with "navTabs" to
		reset styling to a blank state
	*/
	$('ul.nav').attr({"class":"navTabs"});

	/*create tabs from an unordered list using jquery.ui.tabs*/
	$('ul.navTabs').tabs(
		{ fx: { height: 'toggle', opacity: 'toggle' } }
	);
 
});

Ajax タブ用の JavaScript コードは、前の DHTML タブ用のコードより複雑になります。これも同じく、Progressive Enhancement を使用する必要があるためです。2 次ナビゲーション・メニューのマークアップを前に変更したときには (リスト 15 を参照)、リンク URL は変更しませんでした。こうすることによって、JavaScript がオフにされても、これらのリンクはいつものように機能します。その一方、Ajax タブを作成するには、これらのリンクを jQuery UI Tabs が理解するフォーマットに変換しなければなりません。具体的には、Introduction タブと Product Images タブにはインライン・コンテンツがあるため、href プロパティーを #wrapperDivIDAttribute フォーマットに変換する必要があります。その他の 3 つのタブのコンテンツは Ajax によって取得されるため、href プロパティーは前の手順で作成した HTML フラグメント・ファイルを指す必要があります。幸い、jQuery によってこれらのリンクは簡単に変換することができます。

jQuery では、2 次ナビゲーション・メニューの class 属性を変更するのも簡単です。このメニューを構成する ul 要素には多数のスタイル・ルールが追加されていますが、li 要素をタブに変換するときには、元のスタイルを取り消さなければなりません。それには、jQuery を使って ul 要素の class 属性を nav から navTabs に変更します。このように変更した後は、jQuery セレクター・メカニズム ($ 関数) で ul 要素のクラスを設定し直して tabs メソッドを適用することができます。

基本的に作業はこれですべてですが、CSS についてはまだ終わっていない作業がいくつかあります。まず、このページの Ajax バージョンにだけ適用する必要があるスタイルを無効にしなければなりません。それには、最初に行ったのと同じように noscript スタイル・ブロックをページの先頭に追加しますが、今回はページに追加されたダミーの画像スライドショー・マークアップを非表示にするスタイル・ルールをもう 1 つ追加します。追加後の noscript スクリプト・ブロックはリスト 17 のようになります。

リスト 17. detailB1.html の noscript スタイル・ブロック
<noscript>
	<style type="text/css">
		#CMN .tabContent {
			padding: 0;
			border: 0;
		}
		#CMN .tabContent h2 {
			display: block;
		}
		#CMN #productImages {
			display: none;
		}
	</style>
</noscript>

最後に、jQuery Tabs UI が Ajax コンテンツをタブ付きインターフェースに接続する際の特異性に対処する必要があります。Web ブラウザーでページを表示すると、タブと Ajax タブのコンテンツの間の境界線が 2 倍の太さになっているはずです。jQuery UI Tabs は Ajax コンテンツの各構成部分を上部枠線が設定されたラッパー div 内に配置しますが、ラッパー内の HTML フラグメントには必要な枠線がすでに適用されているため、境界線が二重になるというわけです。これを修正するには、HTML フラグメント・ファイルのラッパー div にもう 1 つクラスを追加した上で、このクラスを持つ要素から上部枠線を削除するというスタイル・ルールを追加します。最終的なコードは、リスト 18 とリスト 19 のとおりです。

リスト 18. 二重の境界線を修正するための CSS 宣言
#CMN .tabContent.noTop {
	border-top: 0;
}
リスト 19. HTML フラグメント・ファイルに適用した CSS クラス
<div class="tabContent noTop" id="moreDetails">
 
	<h2>More Details</h2>

	<!--paragraphs of text content here-->

</div>

マルチページ・バージョンを再検討する

Product Details バージョン B の 2.1 を Web ブラウザーで表示してみてください。外観も振る舞いもバージョン A とまったく同じはずです (図 5図 6)。しかしその裏では、バージョン B のほうが遥かに効率的になっており、Ajax コンテンツは、ユーザーが該当するタブをクリックするまで一切ロードされることはないため、帯域幅が節約されます。ただし残念ながら、画像については大幅な帯域幅の節約には至りませんでした。このカスタム Ajax コードの動作では、jCarousel スライドショーのすべての画像がデフォルトでロードされるためです。しかしページのロード時間は短縮されています。ブラウザーはページの残りの部分がレンダリングされるまで、これらの画像をサーバーに要求しません。そのため、遅い接続では、ユーザー・エクスペリエンスが大幅に改善されます。

追加の作業で、Product Images タブが選択されるまで画像を自動的にロードしないというソリューションを作ることができました。これで、ユーザーが画像を表示しないという選択をした場合、画像のロードで帯域幅が浪費されることはなくなります。このようなソリューションは JavaScript コードを複雑にしますが、製品の画像数が多くなればなるほど、この作業を行うだけの価値が出てくるはずです。

Product Details のバージョン A とバージョン B とでの最終的な違いが現れてくるのは、JavaScript を無効にした場合です。バージョン A は単一のスクロール・ページに戻る一方、バージョン B は複数のページに戻ります。

まとめ

連載の第 3 回では、Progressive Enhancement の原則を適用するために努力した結果、最新化された魅力的で極めて使用に適した Ajax インターフェースを実現しました。また、jQuery とその多彩なプラグインの威力についての知識も十分に身に付いたはずです。この記事で学んだスキルを使うと、サイトの改善を続けることができます。例えば、jQuery Cookie プラグインを使用すると、ブラウザーは最後に選択されたタブを記憶して、ユーザーがページに戻ってきたときにそのタブを表示することができます。さらに、Purchase Confirmation ページにタブ付きインターフェースをビルドすることも可能です。組み合わせでの販売、注文履歴、請求履歴ごとに 1 つのタブを表示するなど、可能性は尽きません。

実際には、サイト用に 3 つのバージョンの Product Details をビルドすることはないでしょう。1 つの理由として、それにはかなりの時間がかかるからです。また別の理由として、異なるインターフェースを混在させるとユーザーを混乱させることにもなり兼ねない、ということがあります。しかしこの記事で実証したように、jQuery などのオープンソース・ツールが持つ威力と柔軟性を利用すれば、Ajax、Progressive Enhancement、そしてユーザーを中心に置いたデザインのすべてを容易に達成することができます。


ダウンロード

内容ファイル名サイズ
Source code for the original demo appwa-aj-overhaul3OnePointOne.zip797KB
Source code for the retrofitted demo appwa-aj-overhaul3TwoPointOne.zip889KB

参考文献

学ぶために

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

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

  • jQuery のメイン Web サイトで、jQuery をダウンロードして、その原理について学んでください。このサイトには、追加プラグインも用意されています。この記事を書いている時点での jQuery の最新バージョンは 1.2.3 です。
  • jQuery プラグインの 1 つ、jQuery UI Tabs では、インラインまたは Ajax コンテンツをタブ付きインターフェースにラップすることができます。このプラグインは、カスタマイズ可能なウィジェットとユーザー・インターフェース・コンポーネントをセットにした新しい jQuery UI コレクションに含まれています。
  • jCarousel も同じく jQuery プラグインで、インラインまたは Ajax イメージをスライドショー/カルーセル・インターフェースにロードできます。
  • jQuery Cookie は、 jQuery UI Tabs に追加できる別のプラグインです。このプラグインを追加すると、ブラウザーが最後に選択されたタグを「記憶」して、次にユーザーがページに戻ってきたときに表示することができます。

議論するために

その他のダウンロード

  • デモ: Customize Me Now 1.1 (著者の Web サイトで、元のデモ・アプリケーションの動作を確認してください。)
  • デモ: Customize Me Now 2.1 (著者の 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=327423
ArticleTitle=Ajax による改良: 第 3 回 jQuery、Ajax タブ、そしてフォト・カルーセルで既存のサイトを改良する
publish-date=07082008