目次


Ajax と XML

Ajax に共通の 5 つのデザイン・パターン

今日から使える便利な Ajax のデザイン・パターン

Comments

コンテンツシリーズ

このコンテンツは全#シリーズのパート#です: Ajax と XML

このシリーズの続きに乞うご期待。

このコンテンツはシリーズの一部分です:Ajax と XML

このシリーズの続きに乞うご期待。

Ajax は間違いなく、誰もが自らのサイトに結び付けたがる Web 2.0 の流行語です。しかし、この言葉は実際には何を意味するのでしょうか。また、エンジニアはアーキテクチャー・レベルでどのように Ajax をサイトに統合しているのでしょうか。この記事では Ajax の基本を説明し、Web 2.0 開発のベスト・プラクティスとなっている Ajax のデザイン・パターンを紹介します。

まず第一に、Ajax は DHTML (Dynamic HTML) や XMLHTTPRequest オブジェクトなどの一連の技術をひとまとめにした流行語でしかありません。DHTML は、ハイパーテキスト・マークアップ言語 (HTML)、JavaScript コード、そしてカスケーディング・スタイル・シート (CSS) という 3 つの要素の組み合わせです。Web ページで JavaScript コードを使用すると、ページを動的に変更してコンテンツを追加、削除、または変更できます。これがまさに、DHTML の動的な部分です。JavaScript コードは XMLHTTPRequest オブジェクトを使用して、ページがロードされた後にサーバーからのデータを要求します。

サーバーに対する即時データ要求、そしてそのデータを使用するためのページの変更という 2 つの要素の組み合わせは、Ajax と呼ばれるものの本質であり、Web 2.0 サイトの動的性質です。

けれども Ajax という言葉の意味を説明したところで、これらの機能が現実の世界でどのように使われているのか、そしてサイトではどのように使うべきなのかは伝わりません。Ajax の使用方法を理解するには、一連の単純なデザイン・パターンが必要です。「デザイン・パターン」という言葉に馴染みがない方のために説明すると、この言葉は同名タイトルの優れた著書に由来しています (「参考文献」を参照)。エンジニアが直面するさまざまな共通のタスクに対応した実装パターンを記載しているこの著書は、システムの設計方法に関するベスト・プラクティスだけでなく、エンジニアがコードについて語るときに使用できる用語についても説明しています。

この記事では、Ajax に共通の 5 つのデザイン・パターンを紹介します。これらのデザイン・パターンの違いは、サーバーからデータを取得する際に HTML、XML、JavaScript コードのどれを使用するかです。まずは、サーバーからの新しい HTML でページを更新する、最も簡単なパターンから紹介します。

パターン 1: HTML セグメントの置き換え

おそらくもっとも一般的な Ajax タスクは、サーバーに更新済み HTML を要求し、その HTML でページの一部を更新することでしょう。このタスクは、例えば株価情報の更新のように定期的に実行できます。あるいは検索要求に応答するなど、オンデマンドでの更新も可能です。

リスト 1 は、サーバーにページを要求し、そのコンテンツをページの本文にある <div> タグに配置するコードです。

リスト 1. Pat1_replace_div.html
<html>
<script>
var req = null;
function processReqChange() {
  if (req.readyState == 4 && req.status == 200 ) {
    var dobj = document.getElementById( 'htmlDiv' );
    dobj.innerHTML = req.responseText;
  }
}

function loadUrl( url ) {
  if(window.XMLHttpRequest) {
    try { req = new XMLHttpRequest();
    } catch(e) { req = false; }
  } else if(window.ActiveXObject) {
    try { req = new ActiveXObject('Msxml2.XMLHTTP');
    } catch(e) {
    try { req = new ActiveXObject('Microsoft.XMLHTTP');
    } catch(e) { req = false; }
  } }
  if(req) {
    req.onreadystatechange = processReqChange;
    req.open('GET', url, true);
    req.send('');
  }
}

var url = window.location.toString();
url = url.replace( /pat1_replace_div.html/, 'pat1_content.html' );
loadUrl( url );
</script>
<body>
Dynamic content is shown between here:<br/>
<div id="htmlDiv" style="border:1px solid black;padding:10px;">
</div>
And here.<br/>
</body>
</html>

リスト 2 に、上記のコードが要求対象としているコンテンツを示します。

リスト 2. Pat1_content.html
	HTML encoded content goes here.

このページを Firefox でロードすると、図 1 のような結果が表示されます。

図 1. <div> タグが置換されたページ
図 1. <div> タグが置換されたページ
図 1. <div> タグが置換されたページ

リスト 1 のコードに戻って説明すると、まず注目する点は loadUrl() 関数です。サーバーに対して URL を要求するこの関数は、XMLHTTPRequest オブジェクトを使用してサーバーに新規コンテンツを要求しています。この関数はまた、ブラウザーがコンテンツを受信すると呼び出されるコールバック関数 (この例では、processReqChange) も指定しています。

processReqChange 関数は、オブジェクトを調べて要求が完了したかどうかを確かめます。完了している場合は、ページの <div> タグに含まれる innerHTML を応答のテキストに設定します。

<div> タグを動的コンテンツのプレースホルダーとして使用する方法は、Ajax コードの定番です。これらのタグには (この例のように境界線などを追加しない限り) 可視の内容はありませんが、コンテンツの配置場所を示すマーカーとして役立ちます。エンジニアが置換可能なセグメントとして使用するタグには、後で説明するように <span> タグもあります。<div> タグと <span> タグの違いは、前者は (パラグラフのように) 改行を設定しますが、後者はインライン・テキストのセクションに輪郭線を付けるところです。

processReqChange 関数にいったん話を戻すと、この関数では status と readyState の両方の値をチェックすることが重要です。ブラウザーによっては、要求の完了時にのみこの関数を呼び出す場合もあれば、継続的にこの関数を呼び出して要求が実行中であることをコードに知らせる場合もあるからです。

タブ付き表示による変形

このパターンには、表示をタブ付形式で作成するという変形もあります。リスト 3 に、単純なタブ付き Ajax インターフェースを示します。

リスト 3. Pat1_tabs.html
<html>
<script>
var req = null;
function processReqChange() {
  if (req.readyState == 4 && req.status == 200 ) {
    var dobj = document.getElementById( 'tabDiv' );
    dobj.innerHTML = req.responseText;
  }
}

function loadUrl( tab ) {
  var url = window.location.toString();
  url = url.replace( /pat1_tabs.html/, tab );
  ...
}

function tab1() { loadUrl( 'pat1_tab1_content.html' ); }
function tab2() { loadUrl( 'pat1_tab2_content.html' ); }
tab1();
</script>
<body>
<a href="javascript: void tab1();">Tab 1<a> 
<a href="javascript: void tab2();">Tab 2<a>
<div id="tabDiv" style="border:1px solid black;padding:10px;">
</div>
</body>
</html>

最初のタブのコンテンツは、リスト 4 のとおりです。

リスト 4. Pat1_tab1_content.html
Tab 1 content

2 番目のタブのコンテンツは、リスト 5 のとおりです。

リスト 5. Pat1_tab2_content.html
Tab 2 content

ブラウザーでこのページを表示すると、最初のタブが図 2 のように表示されます。

図 2. 最初のタブのコンテンツ
Content for the first tab
Content for the first tab

2 番目のタブのリンクをクリックすると、ブラウザーが 2 番目のタグのコンテンツを取得し、図 3 のようにタブ・エリアに表示します。

図 3. 2 番目のタブのコンテンツ
図 3. 2 番目のタブのコンテンツ
図 3. 2 番目のタブのコンテンツ

ユーザーからの要求を受けて、新たなコンテンツで表示の一部を更新するというのは (この例では、タブ付き表示のように錯覚させています)、このデザイン・パターンの真髄を表す使い方です。アプリケーション側からすると、カスタマーがダウンロードするページを大幅に軽量化することが可能で、カスタマーがオンデマンドでコンテンツにアクセスできるという利点があります。

Ajax が登場する以前の一般的な手法では、ページに最初のタブと 2 番目のタブを両方持たせ、要求に応じてタブの表示と非表示を切り替えることになります。つまり、2 番目のタブは決して表示されることがないとしても作成されるため、サーバーの時間と帯域幅がどちらも無駄になります。この新しい Ajax の手法を使えば、2 番目のタブはユーザーが要求したときにだけ作成されるようになります。

「続きを読む」の変形

HTML の置き換えに関しては、図 4 に示す「続きを読む」(Read more) バージョンもあります。

図 4. 私の退屈なブログ・エントリーの Read more リンク
図 4. 私の退屈なブログ・エントリーの Read more リンク
図 4. 私の退屈なブログ・エントリーの Read more リンク

例えば、この犬の散歩でこの後、どんなことが起こったのか読みたいとします。その場合、Read more リンクをクリックすると、リンクは図 5 のように完全に夢中にさせるストーリーに置き換わります。

図 5. Read more リンクをクリックした後のページ
図 5. Read more リンクをクリックした後のページ
図 5. Read more リンクをクリックした後のページ

カスタマーにとって有難いのは、ページを更新しなくてもシームレスに詳細な内容が表示されるという点です。

リスト 6 に、このページのコードを示します。

リスト 6. pat1_readmore.html
<html>
<script>
var req = null;
function processReqChange() {
  if (req.readyState == 4 && req.status == 200 ) {
    var dobj = document.getElementById( "moreSpan" );
    dobj.innerHTML = req.responseText;
  }
}

function loadUrl( url ) { ... }

function getMore()
{
  var url = window.location.toString();
  url = url.replace( /pat1_readmore.html/, 'pat1_readmore_content.html' );
  loadUrl( url );
}
</script>
<body>
<h1>Walking the dog</h1>
I took my dog for a walk today. 
<span id="moreSpan">
<a href="javascript: void getMore()">Read more...</a>
</span>
</body>
</html>

リスト 7 は、Read more セクションのコンテンツです。

リスト 7. pat1_readmore_content.html
It was a nice day out. Warm and sunny. My dog liked getting out for a stretch.

このコードでは、<div> タグの代わりに <span> タグを使用する方法を示しています。どちらのタグを使用するかはユーザー・インターフェース (UI) の要件によって異なりますが、コードを見るとわかるように、いずれのタグも簡単に使用できます。

ページに新しい HTML を取得する方法は別として、ページでデータを使って実際にインテリジェントなことを行うために JavaScript コードが必要な場合についてはどうでしょう。データを体系的にブラウザーに渡すには何を使えばいいのでしょうか。その答えはもちろん、XML です。

パターン 2: XML データの読み取り

どういうわけか、Ajax は XML の代名詞となっていますが、Ajax には XML が絶対的に必要というわけではありません。上記の例からわかるように、Ajax ではそのままのテキストや HTML (または XHTML) コードのフラグメントでも返すことができます。しかし、XML を送信すると、それに見合った良い点があります。

リスト 8 に示すのは、本に関するレコードをサーバーに要求し、そのデータをページ内の表に表示する Ajax コードです。

リスト 8. pat2_xml.html
<html>
<head>
<script>
var req = null;
function processReqChange() {
  if (req.readyState == 4 && req.status == 200 && req.responseXML ) {
    var dtable = document.getElementById( 'dataBody' );
    var nl = req.responseXML.getElementsByTagName( 'book' );
    for( var i = 0; i < nl.length; i++ ) {
      var nli = nl.item( i );
      var elAuthor = nli.getElementsByTagName( 'author' );
      var author = elAuthor.item(0).firstChild.nodeValue;
      var elTitle = nli.getElementsByTagName( 'title' );
      var title = elTitle.item(0).firstChild.nodeValue;

      var elTr = dtable.insertRow( -1 );

      var elAuthorTd = elTr.insertCell( -1 );
      elAuthorTd.innerHTML = author;

      var elTitleTd = elTr.insertCell( -1 );
      elTitleTd.innerHTML = title;
} } }

function loadXMLDoc( url ) {
  if(window.XMLHttpRequest) {
    try { req = new XMLHttpRequest();
    } catch(e) { req = false; }
  } else if(window.ActiveXObject) {
    try { req = new ActiveXObject('Msxml2.XMLHTTP');
    } catch(e) {
    try { req = new ActiveXObject('Microsoft.XMLHTTP');
    } catch(e) { req = false; }
  } }
  if(req) {
    req.onreadystatechange = processReqChange;
    req.open('GET', url, true);
    req.send('');
  }
}

var url = window.location.toString();
url = url.replace( /pat2_xml.html/, 'pat2_xml_data.xml' );
loadXMLDoc( url );
</script>
</head>
<body>
<table cellspacing="0" cellpadding="3" width="100%">
<tbody id="dataBody">
<tr>
  <th width="20%">Author</th>
  <th width="80%">Title</th>
</tr>
</tbody>
</table>
</body>
</html>

リスト 9 は、このページのデータです。

リスト 9. pat2_xml_data.xml
<books>
  <book>
    <author>Jack Herrington</author>
    <title>Code Generation in Action</title>
  </book>
  <book>
    <author>Jack Herrington</author>
    <title>Podcasting Hacks</title>
  </book>
  <book>
    <author>Jack Herrington</author>
    <title>PHP Hacks</title>
  </book>
</books>

ブラウザーでこのページをロードすると、図 6 のように表示されます。

図 6.XML の表示ページ
図 6.XML の表示ページ
図 6.XML の表示ページ

このページと前のパターンのページとの大きな違いは、processReqChange 関数にあります。上記では、responseText の代わりに responseXML が使用されています。responseXML は DOM (Document Object Model) の 1 つで、サーバーからの応答が正しくエンコードされた XML である場合にのみ使用可能になります。

responseXML を使用して XML 文書からの <book> タグのリストを要求し、それぞれのタグから <title> 要素と <author> 要素を取得しています。次に本ごとの表に行を追加し、各行にセルを追加して著者とタイトルのデータを含めています。

これはかなり初歩的な XML データの使用方法です。より高度な JavaScript コードでは、返されたデータに基づいてクライアント側でソートしたり、検索することもできます。

残念ながら XML データを転送すると、ブラウザーが XML 文書全体を解析するのに時間がかかるというマイナス面があります。また、XML 内のデータを検索するための JavaScript コードも複雑になります (リスト 8 を参照)。そこで、代わりの方法として使えるのは、サーバーに JavaScript コードを要求することです。

パターン 3: JavaScript データの読み取り

サーバーに対して JavaScript データを要求するというのは、JSON (JavaScript Object Notation) というしゃれたコード名で知られる手法です。JavaScript データを返す利点は、ブラウザーにとっては JavaScript データ構造のほうがはるかに使いやすく、効率的に解析および作成できるというところにあります。

サーバーからの XML を読み取るリスト 8 のコードを、サーバーからの JavaScript データを読み取るコードに変更すると、新しいコードはリスト 10 のようになります。

リスト 10. pat3_js.html
<html><head><script>
var req = null;
function processReqChange() {
  if (req.readyState == 4 && req.status == 200 ) {
    var dtable = document.getElementById( 'dataBody' );
    var books = eval( req.responseText );
    for( var b in books ) {
      var elTr = dtable.insertRow( -1 );

      var elAuthorTd = elTr.insertCell( -1 );
      elAuthorTd.innerHTML = books[b].author;

      var elTitleTd = elTr.insertCell( -1 );
      elTitleTd.innerHTML = books[b].title;
} } }

...

HTML コード全体はそのままで、processReqChange 関数がサーバーから返された JavaScript データである eval を読み取るように変更されているだけです。processReqChange 関数は、eval から読み取った JavaScript オブジェクトをデータ・ソースとして使用し、このデータ・ソースが表に追加されます。

リスト 11 は、サーバーからの JavaScript データです。

リスト 11. pat3_js_data.js
[ { author: 'Jack Herrington', title: 'Code Generation in Action' },
{ author: 'Jack Herrington', title: 'Podcasting Hacks' },
{ author: 'Jack Herrington', title: 'PHP Hacks' }
]

多くの Ajax アプリケーション・エンジニアが XML ではなく、JavaScript コードを使ってデータをエンコードする理由は一目瞭然です。JavaScript コードのほうが、読みやすく管理しやすいだけでなく、ブラウザーにとっても処理が簡単だからです。

この一連のデータ収集と表示では、Ajax の鍵となっているのが現行データの表示であることがわかります。ここで重要なのは、データが現行のものであることです。そこで質問です。サーバーから常に新しいデータを取得するようにするには、どうすればいいでしょう。

パターン 4: ブラウザー・キャッシュの回避

ブラウザーは常に Web トラフィックの最適化を試みるため、同じ URL を 2 度要求すると、ブラウザーはページを再度要求する代わりに、ブラウザー・キャッシュに保管されたページを使用するでしょう。そこで、Ajax アプリケーションに共通のパターンとして、URL 内の要素をランダム化し、ブラウザーがキャッシュに入れられた結果を返さないようにするというパターンがあります。

私が気に入っているのは、現在時刻の数値を URL に追加するという手法です。リスト 12 に、この手法を示します。

リスト 12. pat4_cache.html
<html>
<script>
...

function loadUrl( url ) {
  url = url + "?t="+((new Date()).valueOf());
  ...
}

...

上記はリスト 1 のコードに、URL ストリングに JavaScript によるテキスト操作を加えたものです。URL に、時刻の値を持つ新しいパラメーター t を追加しています。サーバーがこの値を認識するかどうかは問題ではありません。これは、ブラウザーが URL に基づくページ・キャッシュを無視することを確実にするための手段でしかないからです。

パターン 5: 複数 HTML セグメントの置換

最後に紹介するパターンは最初のパターンの高度なバージョンで、<div> タグをサーバーからのコンテンツに置き換えるというパターンです。Web アプリケーションでは、ユーザー入力に応答して、複数の表示域を更新しなければならないことが問題になる場合がよくあります。例えば、表示の一部に最新の相場を表示し、別の部分に最新の株価リストを表示する株価情報のアプリケーションがあったとします。

この複数の表示域を更新するために私が使用したのは、両方のセクションのデータが含まれるサーバーからの XML 応答です。次に正規表現を使用して、応答から個別のセクションを取り出しています。リスト 13 に、この手法を示します。

リスト 13. pat5_multi_segment.html
<html>
<script>
...

function loadUrl( url ) {
  url = url + "?t="+((new Date()).valueOf());
  ...
}

...

リスト 14 に、サーバーからのデータを示します。

リスト 14. pat5_data.xml
<segments>
  <one>Content for segment one</one>
  <two>Content for segment <b>two</b></two>
</segments>

ブラウザーでこのページをロードすると、図 7 のように表示されます。

図 7. サーバーからのデータで更新された 2 つのセグメントの表示
図 7. サーバーからのデータで更新された 2 つのセグメントの表示
図 7. サーバーからのデータで更新された 2 つのセグメントの表示

ページ・コードでは、XML 応答を使用することもできました。サーバーから返される内容は整形式の XML だからです。ただし、XML コードから個別のセグメントを解読するには正規表現を使用したほうが簡単なので、このようなコードになりました。

まとめ

Ajax は非常に強力であると同時に誤解も多く、誤った方法で使用されています。Web アプリケーションで Ajax を使用する出発点としてふさわしいのは、この記事で紹介したパターンです。ただし、ここに記載したコードを使用するだけでなく、Web 2.0 革命に伴って生まれた、優れた Ajax および Web UI ライブラリーもいくつか調べてみることをお勧めします。なかでも代表格は、Prototype.js ライブラリーです。このライブラリーはサーバーとデータをやり取りするための簡単なメソッドから、特定ブラウザーに依存せずに Web ページ・コンテンツを更新するメソッドまで提供しています。これらのライブラリーを使用する利点は、熱心なエンジニアが多種多様なブラウザーとプラットフォームでメソッドを管理およびテストすれば、作業の負担と頭痛の種を大幅に取り除ける可能性があることです。

アプリケーションに動的振る舞いを追加するには、どのように取り掛かるにしても、この記事で紹介したパターンが示す Ajax の調査は不可欠です。


ダウンロード可能なリソース


関連トピック

  • developerWorks XML ゾーン: developerWorks XML ゾーンで XML のすべてについて学んでください。
  • Prototype.js: Prototype.js を試してみてください。設計および実装に優れたこの JavaScript ライブラリーは、ブラウザーに依存しない Ajax オブジェクトと DHTML オブジェクトを提供します。自力で Ajax JavaScript コードに取り掛かる代わりに、時間を割いて Prototype.js の使用を検討するだけの価値は十分あります。
  • 『オブジェクト指向における再利用のためのデザインパターン』(Erich Gamma 他による共著、Addison-Wesley (原書)、ソフトバンククリエイティブ (邦書)、1995年): ソフトウェア・エンジニアリングにおけるパターンの使用についての権威ある著書を読んでください。
  • IBM XML 認証: XML や関連技術の IBM 認定開発者になる方法について調べてください。
  • Ajaxian: この優れた情報源で、Ajax、そして Ajax を使用するフロント・エンドのウィジェットについての最新開発情報を入手してください。
  • Yahoo! UI ライブラリー: Yahoo! の UI ライブラリーを入手してください。これは、Web アプリケーションで Ajax と併せて使用できる Web UI キットです。
  • ATLAS ライブラリー: Microsoft が Ajax ブラウザー互換性レイヤーに採用した ATLAS ライブラリーを試してみてください。このライブラリーは Microsoft® ASP.NET を使用していなくても利用できます。
  • Scriptaculous: Prototype.js ライブラリーを使用した、ブラウザーに依存しないエフェクトおよびウィジェットのライブラリーをダウンロードしてください。
  • Moo.fx: Prototype.js をベースにした軽量なウィジェット・ライブラリーを入手してください。

コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=XML, Web development
ArticleID=249239
ArticleTitle=Ajax と XML: Ajax に共通の 5 つのデザイン・パターン
publish-date=03062007