咖啡日语论坛

 找回密码
 注~册
搜索
查看: 8654|回复: 9

今からでも遅くない Ajax基本のキ

[复制链接]
发表于 2007-5-25 09:39:17 | 显示全部楼层 |阅读模式
回复

使用道具 举报

 楼主| 发表于 2007-5-25 09:39:48 | 显示全部楼层
What is Ajax ?


Web 2.0,WebAPI,リッチ・クライアント,SOA(サービス指向アーキテクチャ),アジャイル開発…などなど,相も変わらずWebアプリケーション開発の現場には,様々なキーワードが渦巻いています。現在その中でも特に注目されているのが,Ajax(Asynchronous JavaScript AndXML)です。
 Ajaxは,2005年2月にAdaptive Path社のJesse James Garrett氏が自身のコラム「Ajax: A New Approach to Web Applications」上で著したものが注目を浴び,記事や書籍などで紹介されたことから,急速に認知されるようになったキーワードです。筆者などは当時,「しょせんただのBuzzword(はやり言葉/空騒ぎ)だろう」と比較的冷ややかに思っていたものです。
 しかし,その後1年を経て,いまだに衰える気配はありません。昨今では,ASP.NET,PHP,Javaなど主要なサーバーサイド開発環境の世界でもAjaxに対応したフレームワークやライブラリが登場しつつあり,Ajaxも単なる「流行もの」から現実的な使い方が模索される時期に移行しつつあるように思われます。
 本連載では,筆者同様,「しょせん流行ものだろう」…とちょっと乗り遅れてしまった皆さんのために,Ajax技術を基礎からおさらいしてみることにします。Buzzwordと忌避してきた皆さんも,もはや一過性とはいえなくなったAjax技術に,今一度注目してみてはいかがでしょう?
Ajaxとは? くどくどとしくみの紹介を先行するよりも,まずはAjax技術を利用して構築したアプリケーションの例をご覧戴いた方がわかりやすいかもしれません。図1[拡大表示]は,Ajax技術を使った例として取り上げられることの多い地図アプリケーションGoogleマップの例です*1
 ブラウザ上のマウスドラッグによって地図の表示範囲を移動させたり,拡大/縮小率を変更したりできます。従来であれば,地図上の表示や縮小率を切り替えるたびに,いちいち画面(ページ)を切り替えなければならなかった手間や,そのために自分が一番見やすい位置,縮小率に合わせるのが難しかったことを思えば,はるかに直感的な操作性を提供していることがおわかりになるでしょう。
 このような,一見,Flash技術を利用しなければ実現できそうもないリッチなコンテンツを,(特別なプラグインなどを導入することなく)標準的なブラウザの機能だけで実現できてしまうあたりが,Ajax技術が急速に注目を浴びたゆえんともいえます。昨今では,Ajax技術を利用したアプリケーション(コンテンツ)が,それこそ雨後の筍のように登場しています(表1[拡大表示])。
Ajaxのしくみ さて,Googleマップのような例を見てみると,いかにも革新的な技術のようにも思えるかもしれません。しかし,繰り返しになりますが,Ajax技術がこれだけ注目を浴びたのは,こうしたリッチな効果を,ブラウザ標準の技術だけで実現できるという点にあります。Ajaxとは決して新しい技術ではないのです。
 図2[拡大表示]は,Ajax技術によるクライアント/サーバー間の通信を大まかに表したものです。
 Ajaxでは,ページ上でボタンがクリックされたりテキストボックスの内容が変更されたりすることによって発生するイベント(出来事)をJavaScriptで捕捉し,必要な情報をXMLHttpRequestオブジェクト(HTTP通信を行うためのオブジェクト)を使ってサーバーに送信します。サーバー側で処理された結果は,コールバック関数と呼ばれる関数で非同期に受け取り,DHTML(ダイナミックHTML)技術を使って,ページの必要な部分だけを動的に更新します。
 「非同期に」とは,処理を行っている間,クライアント側の処理が中断されないこと。すなわちサーバー側の処理とクライアント側での動作とはそれぞれに別物として動作していることを意味します。
 これに対して従来のWebアプリケーションでは,ブラウザでイベントが発生し,サーバーにリクエストを送信すると,そのたびにサーバーから応答(レスポンス)として送られてきたページ全体を読み込んで再描画しなくてはなりません。また,通信の発生によってユーザーの処理が中断されますから,スムーズな操作を実現できませんでした。
 いかがですか?すごい技術とフタを開けてみれば,なんてことはない。Ajaxとは,JavaScriptにDHTML,XMLと,いずれもいい加減,使い古された技術の寄せ集めであることがおわかりになるでしょう。XMLHttpRequestオブジェクトによる非同期通信でさえ,なんら目新しいものではなく,すでに何年も前からいくらでも利用されてきたものです。
 もしもAjaxに新規性があるとすれば,JavaScript,DHTML,XMLという組み合わせに「Ajax」と命名したことで,改めてリッチ・コンテンツ実現の可能性を示した点にあるといえるでしょう。使い古された技術がただ名前を新しくしただけでブレイクするというのはナンセンスだ,ゆえにAjaxとはやはりBuzzwordなのだ,という意見もあるかもしれません。
 しかし,よくよく考えてもみれば,Ajaxが新しい技術か古い技術かという議論自体がナンセンスではなのではないでしょうか。Ajaxが新しい技術であるにせよそうでないにせよ,Webアプリケーション構築のうえでの一つの可能性が,今ここに再発掘されたわけです。これを利用しない手はありません。

[ 本帖最后由 bgx5810 于 2007-5-25 09:45 编辑 ]
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-5-25 09:44:53 | 显示全部楼层
基本的なAjaxアプリケーションを作成してみよう(前編)



 本連載第1回ではAjax技術の概要と具体的なAjaxアプリケーションについて紹介したわけですが,やはり概論だけではなかなかイメージもわきにくいものです。そこで今回から,具体的なサンプルアプリケーションを通じて,実際のAjax開発を体感してみることにしましょう。
 今回紹介するのは,ごくシンプルな検索アプリケーションです。テキストボックスにISBNコード(書籍を一意に特定するコード)を入力すると,対応する書籍名を表示します。なお,本連載ではサーバーサイドの技術としてPHP(PHP:HypertextPreprocessor)を使用しますが,Ajaxはサーバーサイドで使用する技術を制限するものではありません。例えば,ASP.NET,JSP&サーブレット,Perl/CGIのようなサーバーサイド技術を利用しても問題ありません*1
従来の方法で作成してみよう 従来のWebアプリケーションとAjaxアプリケーションとの違いを理解するために,まずPHPだけで検索アプリケーションを実装してみることにしましょう(リスト1)。もっとも,本連載のテーマはあくまでAjaxですから,PHPそのものについては,最低限動作を理解する程度の説明に留めます。コードの流れはプログラム内のコメントを参照いただくとして,構文などの詳細については,拙著「独習PHP」「10日でおぼえるPHP5入門教室」(翔泳社)などの専門書を参考にしてください。
リスト1●従来型Webアプリケーションのコードの例(search.php)<html>
<head>
<title>書籍検索</title>
</head>
<body>

<form method="GET" action="search.php">
ISBNコード:
<input type="text" name="isbn" size="20" />
<input type="submit" value="送信" />
</form>
<?php
/* 入力されたISBNコードをキーに対応する書名($result)を取得。
    通常のアプリケーションでは、ここでデータベースへの検索処理などを行う */
switch($_GET['isbn']){
  case '4-7981-1070-1' :
    $result='XMLデータベース入門 NeoCore/XprioriでXMLDBを極める!';
    break;
  case '4-88337-491-2' :
    $result='書き込み式 SQLのドリル?ドンドン身に付く、スラスラ書ける';
    break;
  case '4-7980-1270-X' :
    $result='Pocket詳解PHP辞典';
    break;
  default :
    $result='不明';
}
sleep(3);  // 3秒休止(待ち時間を体感するためのダミー)
print($result);  // 取得した結果を出力
?>
</body>
</html>
 リスト1のPHPプログラム(search.php)をWebサーバーに配置したら,ブラウザからアクセスして,表示されたテキストボックスに適当なISBNコードを入力してみましょう(例えば「4-7981-1070-1」)。[送信]ボタンをクリックすると,数秒をおいた後,フォーム下部に対応する書名が表示されれば成功です(図1[拡大表示])。

[クリックで拡大]
 動作に際しては,以下の点に注目してください。
・結果を表示する際に,ページ「全体」が書き換えられる
・結果を表示する際に,もともとの入力値がクリアされている
・サーバー通信の間は,クライアント側ではほかの処理を行えない

 これらはいずれも,従来型Webアプリケーションの特徴であり,問題点でもあります。従来型Webアプリケーションでは,ページのごく一部を書き換える場合にも常にページ全体をリフレッシュする必要があります。そのため,クライアント/サーバー間におのずと無駄なトラフィックが発生し,通信前のデータも保持できません(正確には,保持することは可能ですが,そのためにはアプリケーション側でなにかしらの仕組みを実装する必要があります)。また,従来型のWebアプリケーションは「同期」処理が基本ですから,クライアントはサーバーに処理を渡した後,サーバーから応答を得るまで次の動作を行えません。つまり,通信のたびにエンドユーザーは処理を中断されてしまいます。
Ajax技術で作り直してみよう 以上の問題点を理解したうえで,同様のアプリケーションをAjax技術で実装してみましょう。Ajaxアプリケーションを実装する場合,大きくクライアント側で動作するファイル(HTMLファイル,リスト2)とサーバー側で動作するファイル(この場合はPHPスクリプト,リスト3)が必要になります。
リスト2●AjaxアプリケーションのHTMLファイル(searchAjax.html)<html>
<head>
<meta http-equiv="Content-Type" c>
<title>書籍検索</title>
<script language="JavaScript">
<!--
// [送信]ボタンをクリック時の処理を定義
function send() {
   // 非同期通信を行うためのXMLHttpRequestオブジェクトを生成
  try {
    xmlReq = new ActiveXObject("Microsoft.XMLHTTP");
  } catch(e) {
    xmlReq = new XMLHttpRequest();
  }
   // サーバーからの応答時の処理を定義(結果のページへの反映)
  xmlReq.onreadystatechange = function() {
    var msg = document.getElementById("result");
    if (xmlReq.readyState == 4) {
      if (xmlReq.status == 200) {
        msg.innerHTML = xmlReq.responseText;
      } else {
        msg.innerHTML = "通信に失敗しました。";
      }
    } else {
      msg.innerHTML = "通信中…";
    }
  }
  
   // サーバーとの通信を開始
  xmlReq.open("GET","searchAjax.php?isbn="
                    + encodeURI(document.fm.isbn.value),true);
  xmlReq.send(null);
}
//-->
</script>
</head>
<body>
<form name="fm">
ISBNコード:
<input type="text" name="isbn" size="30" />
<input type="button" value="送信"  />
<div id="result" />
</form>
</body>
</html>


リスト3●Ajaxアプリケーションのサーバーサイド・プログラム(searchAjax.php)
<?php
// 出力/内部文字コードをUTF-8に設定
mb_http_output('UTF-8');
mb_internal_encoding('UTF-8');
/* 入力されたISBNコードをキーに対応する書名($result)を取得。
    通常のアプリケーションでは、ここでデータベースへの検索処理などを行う */
switch($_GET['isbn']){
  case '4-7981-1070-1' :
    $result='XMLデータベース入門 NeoCore/XprioriでXMLDBを極める!';
    break;
  case '4-88337-491-2' :
    $result='書き込み式 SQLのドリル?ドンドン身に付く、スラスラ書ける';
    break;
  case '4-7980-1270-X' :
    $result='Pocket詳解PHP辞典';
    break;
  default :
    $result='不明';
}
sleep(3);  // 3秒休止(待ち時間を体感するためのダミー)
print($result);  // 取得した結果を出力
?>


 処理の流れは図2[拡大表示]のようになります。

[クリックで拡大]
 コードの詳細は次回以降に改めて紹介するとして,まずはsearchAjax.htmlからアプリケーションに実際にアクセスしてみましょう(図3[拡大表示])。

[クリックで拡大]
 図の見かけは図1とほとんど変わりませんが,動作に際して以下の点が異なっています。
・結果を表示する際に,ページの「必要な個所だけ」が書き換えられる
・結果を表示する際に,もともとの入力値も保持される
・サーバー通信の間も,クライアント側では並行して別の処理を行える

 Ajaxでは,サーバーからの処理結果を反映する際に,ページ全体を書き換えるのではなく,必要な個所だけを書き換えます。そのため,通信に伴うページのチラツキが発生することもありません。もちろん,常にページ全体の情報を送信する従来型のWebアプリケーションに比べて通信量が減少しますので,ページの内容によってはパフォーマンスを改善できます。
 また,連載第1回に紹介したように,Ajaxアプリケーションでは「非同期」処理が基本です。つまり,サーバーがなにかしらの処理を行っている間にもクライアントはその終了を待つ必要はなく,継続して自分の作業を行うことができます。
今回のまとめ いかがですか。従来型のWebアプリケーションと比べて,Ajax技術がどのような点を改善しているのかをイメージできたでしょうか。表1に,連載の第1~2回を通じてわかったAjaxの利点をまとめておきます。

表1●Ajax技術における従来型アプリケーションからの主な改善点
ユーザビリティの向上Webアプリケーションにありがちな通信ごとの画面のチラツキがない
サーバー処理の間もクライアント側の操作が中断されない(非同期処理)
パフォーマンスの改善必要なコンテンツのみを更新するので,クライアント/サーバー間のトラフィック量を軽減できる;
サーバーサイドで行っていた処理をクライアント側に分散できる
開発生産性の改善特別なプラグインを導入せずにリッチコンテンツを実現できる
標準的なJavaScriptやXMLの知識のみで開発が可能
アプリケーション開発者が新しい知識を習得する必要がない

 Ajaxは従来型のWebアプリケーションの問題点を解消するだけではありません。Ajaxを利用することで,従来型のWebアプリケーションでは実現しにくかった機能を実装することも可能になります。例えば,メモ帳アプリケーションにおいて,現在編集中のメモを定期的な間隔で自動保存するような機能を想定してみると良いでしょう。このような機能は,「同期処理」が前提となる従来型アプリケーションでは実現しにくいものでした。しかし,Ajaxを利用することで,エンドユーザーにストレスをかけることなく(処理そのものを意識させることなく),容易に実装することが可能になります。
 次回は,今回紹介したAjax対応アプリケーションについて,コードをより詳細に眺めていく予定です。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-5-25 09:46:49 | 显示全部楼层
基本的なAjaxアプリケーションを作成してみよう(中編)


 前回は,従来型のPHPアプリケーションとAjaxアプリケーションを比較することで,Ajaxアプリケーションが従来型アプリケーションのどのような点を改善するものなのかを概観しました。今回は,前回紹介したコードを詳しく見ていくことにします。
 本連載のテーマであるAjaxという意味で注目する必要があるのは,クライアント側のコード(リスト1)です。サーバーサイド・アプリケーション(searchAjax.php)は前回見たように,クライアントから受け取ったISBNコードに基づいて対応する書名を出力するだけのごくシンプルなプログラムなので説明は省略します。
リスト1●AjaxアプリケーションのHTMLファイル(searchAjax.html)<html>
<head>
<meta http-equiv="Content-Type" c>
<title>書籍検索</title>
<script language="JavaScript">
<!--
// [送信]ボタンをクリック時の処理を定義
function send() {
   // 非同期通信を行うためのXMLHttpRequestオブジェクトを生成
  try {
    xmlReq = new ActiveXObject("Microsoft.XMLHTTP");
  } catch(e) {
    xmlReq = new XMLHttpRequest();
  }
   // サーバーからの応答時の処理を定義(結果のページへの反映)
  xmlReq.onreadystatechange = function() {
     var msg = document.getElementById("result");
     if (xmlReq.readyState == 4) {
      if (xmlReq.status == 200) {
        msg.innerHTML = xmlReq.responseText;
      } else {
        msg.innerHTML = "通信に失敗しました。";
      }
    } else {
      msg.innerHTML = "通信中…";
    }
  }
  
   // サーバーとの通信を開始
  xmlReq.open("GET","searchAjax.php?isbn="
                    + encodeURI(document.fm.isbn.value),true);
  xmlReq.send(null);
}
//-->
</script>
</head>
<body>
<form name="fm">
ISBNコード:
<input type="text" name="isbn" size="30" />
<input type="button" value="送信"  />
<div id="result" />
</form>
</body>
</html>
クライアント側のイベントを捕捉して処理を起動 Ajaxの処理が起動するきっかけになるイベント(event)とは,ユーザーのアクション,またはブラウザの動作によって発生する「できごと」のことです。クライアントサイド・スクリプト(JavaScript)では,ページ上で発生したイベントをトリガー(trigger=きっかけ)にして処理を起動するイベントドリブン(イベント駆動型)モデルを採用しています。
 JavaScriptで利用可能な主なイベントを,表1に挙げます(ただし,ブラウザの種類やバージョンによって認識できないイベントもあります)。リスト1では,[送信]ボタンがクリックされたタイミング(onclickイベントの発生タイミング)でJavaScriptのsend関数を呼び出し,サーバーとの非同期通信を開始しているというわけです(リスト1の下から5行目)。
[tr]イベント名
発生するタイミング[/tr]
表1●JavaScriptで利用可能な主なイベント
onblur要素からフォーカスが外れた
onchange 要素の内容が変更された
onclick       

要素をクリックした
ondblclick要素をダブルクリックした
onfocus要素にフォーカスがあたった
onkeydown何らかのキーを押下した
onkeyup キーボード上のキーを離した
onloadページがロードされた
onmousedownマウスのボタンを押した
onmousemoveマウス・ポインタを移動した
onmouseoutマウス・ポインタが要素から外れた
onmouseoverマウス・ポインタが要素に乗った
onmouseupマウスボタンを離した
onreset フォームがリセットされた
onselectテキストが選択された
onsubmitフォームがサブミットされた
onunloadページがアンロードされた
非同期通信を管理するXMLHttpRequestオブジェクトを生成する サーバー側との非同期通信を管理するのは,XMLHttpRequestオブジェクトの役割です。XMLHttpRequestオブジェクトを利用することで,これまでブラウザが行ってきたHTTP通信の部分をJavaScriptで制御できるようになります。
 XMLHttpRequestオブジェクトは,多くのブラウザで以前から提供されてきたオブジェクトのひとつですが,ブラウザごとに実装が異なりますので注意が必要です。リスト1の次の部分に注目してください。
  try {
    xmlReq = new ActiveXObject("Microsoft.XMLHTTP");
  } catch(e) {
    xmlReq = new XMLHttpRequest();
  }
 Microsoft.XMLHTTPオブジェクトは,InternetExplorerで利用可能なHTTP通信オブジェクトです。ここではまずブラウザがInternetExplorerであることを前提としてオブジェクトの生成を試み(tryブロック),失敗した場合には改めてXMLHttpRequestオブジェクトの生成を試みています(catchブロック)。XMLHttpRequestオブジェクトは,FirefoxやSafariなどのブラウザで利用可能なHTTP通信オブジェクトです。これによって,クライアントによるHTTP通信オブジェクトの実装の違いを吸収しているわけです。
 なお,以降では便宜上,Internet ExplorerのXMLHTTPオブジェクトも含めて,XMLHttpRequestオブジェクトと呼ぶことにします。XMLHttpRequestオブジェクトで利用可能なプロパティ/メソッドについては,表2にまとめています。これらプロパティ/メソッドの詳細については,登場の都度に紹介します。
[tr]メンバー名概要[/tr]
表2●XMLHttpRequestオブジェクトの主なプロパティ/メソッド。[R|*]は読み取り専用のプロパティ,[R|W]は読み書き可能なプロパティ,[M]はメソッド。(*)が付いたメソッドは,sendメソッドによるHTTP通信が成功した場合にのみ利用可能
[R|W] onreadystatechangeHTTP通信の状態が変化したタイミングで呼び出されるコールバック関数
[R|*] readyStateHTTP通信の状態を取得
[R|*] responseBody サーバーからの応答本体を取得
[R|*] responseText サーバーからの応答をプレーン・テキストとして取得
[R|*] responseXMLサーバーからの応答をXMLDocumentオブジェクトとして取得
[R|*] statusHTTP応答ステータスコードを取得(200=成功,500=内部サーバーエラー,など)
[R|*] statusText HTTPステータス・メッセージを取得
[M] abort() 現在のHTTP要求を中断
[M] getAllResponseHeaders()すべてのHTTP応答ヘッダーを取得(*)
[M] getResponseHeader(header)指定されたHTTP応答ヘッダー(header)を取得(*)
[M] open(method ,url [,async [,user [,password]]]) 指定されたHTTPメソッドでHTTP要求を開始
[M] send(body)HTTP要求を送信
[M] setRequestHeader(header,value) HTTP要求に際して要求ヘッダーを追加
通信状態が変わったときに呼び出されるコールバック関数を定義する コールバック関数は,HTTP通信の状態が変化したタイミングで内部的に呼び出される関数です。コールバック関数を利用することで,以下のような処理を定義することが可能になります。
・応答待ちの状態を画面上に表示する
・サーバーから正常な応答を受け取ったタイミングでページの内容を更新する
・サーバーからエラーを受け取った場合にエラー・メッセージを表示する

 コールバック関数を定義するのは,onreadystatechangeプロパティの役割です。onreadystatechangeプロパティでは,
  xmlReq.onreadystatechange = showResponse;のように,別に定義した関数名(ここではshowResponse)を指定することもできますが,本サンプルではよりシンプルに匿名関数を利用しています。匿名関数とは,その名の通り名前のない関数のことで,以下のようにプロパティ値に直接格納することもできます。
  xmlReq.onreadystatechange = function() {


  ...関数定義...


  }

 リスト1のコールバック関数の処理のコードは次の通りです。処理の流れを図示すると図1のようになります。
var msg = document.getElementById("result");
if (xmlReq.readyState == 4) {  // サーバーから応答が返ってきたか
if (xmlReq.status == 200) {  // サーバーの処理は成功したか
   msg.innerHTML = xmlReq.responseText;  // ページを更新
} else {
   msg.innerHTML = "通信に失敗しました。";  // エラー・メッセージを表示
   }
} else {
  msg.innerHTML = "通信中…";  // 途中経過メッセージを表示
}

 ここでは,readyStateプロパティの戻り値が4(全応答データを取得済み。表3参照)であり,かつ,statusプロパティの戻り値が200(サーバーでの処理が正常終了。表4参照)である場合に,サーバーから送信されてきたデータを取得し,これをid属性の値が"result"である要素に反映させているわけです(リスト1の下から4行目)。サーバーからの応答をプレーン・テキストとして取得するためには,responseTextプロパティを使用します。
[tr]戻り値概要[/tr]
表3●readyStateプロパティの戻り値
0
初期化されていない(openメソッドがまだ呼び出されていない)
1
ロード中(openメソッドは呼び出されたが,sendメソッドはまだ呼び出されていない)
2 ロード済み(sendメソッドは呼び出されたが,応答は取得していない。ステータス/ヘッダー情報はまだ利用できない)
3一部の応答を取得(応答ステータス/ヘッダーは取得できるが,本体は取得できない)
4
全データを取得済み


[tr]HTTPステータス意味[/tr]
表4●HTTP 1.1で利用可能な主なHTTPステータスコード
200 OK
成功
401 Unauthorized
認証を要求
403 Forbiddenアクセスを拒否
404 Not Foundリソースが見つからない
500 Internal Server Error
内部サーバー・エラー
503 Service Unavailableサーバーが利用不可

 サーバーからの応答は取得したものの,サーバー側での処理が失敗している(statusプロパティが200以外)場合にはエラー・メッセージを,そもそもXMLHttpRequestオブジェクトがサーバーからの応答を取得していない(readyStateプロパティが4以外)場合には「通信中…」という経過報告メッセージを,それぞれ表示します。非同期通信であるAjaxの世界では,エンドユーザーに対して現在の状態を示すことは重要です。
 以上,今回はHTTP通信を行うための準備まで紹介しました。次回は引き続き,これらコールバック関数を利用して,実際のHTTP通信を行う部分について説明します。
特別コラム●文字コードはUTF-8に設定 XMLHttpRequestオブジェクトで利用可能な文字コードは,原則として「UTF-8」のみです。Ajaxを利用するに際しては,サーバーサイド・アプリケーション側で必ず文字コードをUTF-8に設定してください。連載第2回のPHPプログラム(searchAjax.php)の場合は,以下の部分が該当します。
  mb_http_output('UTF-8');

  mb_internal_encoding('UTF-8');

ほかの文字コードを使用してマルチバイト文字を送信しようとすると,文字化けが発生する可能性がありますので,注意が必要です。なお,本サンプルでは「.php」ファイルに直接文字コードの設定を記述していますが,もちろん,環境が許すならば,これら文字コードの設定はphp.iniや.htaccess上で行うべきです。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-5-25 09:51:45 | 显示全部楼层
基本的なAjaxアプリケーションを作成してみよう(後編)


 今回は,第3回に引き続きsearchAjax.html(リスト1)とsearchAjax.phpによるシンプルな検索アプリケーションの内容に踏み込んでいきます。前回はXMLHttpRequestオブジェクトを使用するための準備をするところまでを紹介しましたので,今回はいよいよXMLHttpRequestオブジェクトでサーバーに対して要求処理を行ってみることにします。
リスト1●AjaxアプリケーションのHTMLファイル(searchAjax.html)<html>
<head>
<meta http-equiv="Content-Type" c>
<title>書籍検索</title>
<script language="JavaScript">
<!--
// [送信]ボタンをクリック時の処理を定義
function send() {
   // 非同期通信を行うためのXMLHttpRequestオブジェクトを生成
  try {
    xmlReq = new ActiveXObject("Microsoft.XMLHTTP");
  } catch(e) {
    xmlReq = new XMLHttpRequest();
  }
   // サーバーからの応答時の処理を定義(結果のページへの反映)
  xmlReq.onreadystatechange = function() {
     var msg = document.getElementById("result");
     if (xmlReq.readyState == 4) {
      if (xmlReq.status == 200) {
        msg.innerHTML = xmlReq.responseText;
      } else {
        msg.innerHTML = "通信に失敗しました。";
      }
    } else {
      msg.innerHTML = "通信中…";
    }
  }
  
   // サーバーとの通信を開始
  xmlReq.open("GET","searchAjax.php?isbn="
                    + encodeURI(document.fm.isbn.value),true);
  xmlReq.send(null);
}
//-->
</script>
</head>
<body>
<form name="fm">
ISBNコード:
<input type="text" name="isbn" size="30" />
<input type="button" value="送信"  />
<div id="result" />
</form>
</body>
</html>
サーバーに要求を送信する コールバック関数を定義できたら,あとはサーバーに対してリクエストを送信するだけです。リクエストを送信するには,まずopenメソッドでリクエストを開始する必要があります。
  xmlReq.open("GET","searchAjax.php?isbn="
                    + encodeURI(document.fm.isbn.value),true);
openメソッドの構文は,以下の通りです。
  XMLHttpRequest.open(HTTPメソッド ,URL [,非同期モードを使用するか
                      [,ユーザー名 [,パスワード]]])
  ※ 第3引数以降は任意
 比較的シンプルな情報を送信する場合には,HTTPメソッドは"GET"で構いません。GETメソッドでサーバーに対して情報を送信するには,URLの末尾に「?キー名=値」の形式で情報を追加します。値は,encodeURIメソッドであらかじめエンコード処理しておきましょう。本サンプルでは,テキストボックスから入力した値(isbn)を付与しているだけですが,複数の情報を付与したい場合には「&」区切りで複数の情報を列記することも可能です。
 第3引数には,HTTP通信を非同期で行うかどうかを指定します。連載のはじめにも述べたように,Ajaxは非同期通信を前提とした技術ですので,通常はtrueと指定しておけばよいでしょう。もしもここでfalseを指定した場合には,HTTP通信は同期モードで行われ,サーバーが処理を行っている間はクライアント側の処理を継続できません。
 リクエストの準備ができたら,あとはsendメソッドでリクエスト情報をサーバーに送信するだけです。openメソッドはあくまでリクエストの準備を行うだけですので,実際の情報はsendメソッドを呼び出すまで送信されません。
 sendメソッドの引数には,リクエスト時に送信する情報本体を指定します。
  xmlReq.send(null);
sendメソッドの引数は,openメソッドの第1引数として"POST"を指定した場合にのみ指定することが可能です。今回は"GET"メソッドを使用していますので,null(未定義)を指定しておきます。第3回にも説明しましたが,sendメソッドによる要求処理の結果は,onreadystateプロパティで指定したコールバック関数によって取得することができます。
注意
 セキュリティ上の理由から,XMLHttpRequestオブジェクトではopenメソッドのURL(第2引数)として異なるドメインを指定することはできません。使用しているブラウザの種類によっては,(同一ドメインに対する要求であっても)ドメイン名を指定するだけで正しく処理できないものもありますので,注意してください。openメソッドのURLには,サーバー内の相対パスを指定しておくのが無難でしょう。
POSTメソッドで情報を送信するには? GETメソッドはシンプルな情報を送信するには便利ですが,送信できる情報量に制限があるため(具体的なサイズは使用しているサーバーに依存します),数百バイト以上の情報を送信する場合にはPOSTメソッドを使用することをお勧めします。POSTメソッドでは,原則として送信できるデータ量に制限はありません。 POSTメソッドを使用する場合には,searchAjax.htmlのリクエスト送信のコードを以下のように書き換える必要があります。
xmlReq.open("GET","searchAjax.php?isbn="
                  + encodeURI(document.fm.isbn.value),true);
xmlReq.send(null);
xmlReq.open("POST","searchAjax.php",true);
xmlReq.setRequestHeader("content-type",
  "application/x-www-form-urlencoded;charset=UTF-8");
xmlReq.send("isbn=" + encodeURI(document.fm.isbn.value));
 ポイントとなるのは2点です。
(1)Content-Typeヘッダーを設定する
 POSTメソッドで情報を送信する場合には,setRequestHeaderメソッドでContent-Typeヘッダーを指定する必要があります。Content-Typeヘッダーはリクエスト情報の種類を表すための情報です。Content-Typeヘッダーを指定しなかった場合,使用しているブラウザによってはサーバーに正しく情報を送信できませんので,注意してください(例えば,FireFoxなどではContent-Typeヘッダーに「text/xml」が自動セットされます)。「application/x-www-form-urlencoded;charset=UTF-8」は,通常,ブラウザからPOSTデータを送信する場合のデフォルトのContent-Typeです。
(2)データはsendメソッドで指定する
 GETメソッドでは送信すべき情報を「?キー名=値」の形式でURLの末尾に指定しましたが,POSTメソッドではsendメソッドの引数として指定します。GETメソッドの場合と同様,形式は「キー名=値&...」とし,値はencodeURI関数であらかじめエンコードしておく必要があります。
注意
 POSTメソッドで情報を送信した場合,サーバー側のコードにも若干の修正が必要になります。本サンプルの例であれば,「$_GET['isbn']」という部分を「$_POST['isbn']」に変更してください。

★  ★  ★ 以上,3回にわたって,Ajaxによるごく基本的なアプリケーションについて紹介してきました。これでクライアント/サーバー間でプレーン・テキストを受け渡しする方法について理解できたのではないかと思います。
 しかし,サーバー側から応答される情報はプレーン・テキストで表せるようなシンプルな情報ばかりとは限りません。構造化された複合的な情報を受け渡したいというケースのほうがはるかに多いはずです。次回は,Ajaxでそのような構造化データを受け渡しする方法について紹介します。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-5-25 09:54:49 | 显示全部楼层
AjaxでYahoo! ウェブ検索Webサービスを利用してみよう(サーバーサイド編)


 今回からはやや複雑なデータをAjax技術を用いてやり取りしてみることにしましょう。前回までの例では,サーバーから返されるデータは単純な文字列だったわけですが,もちろん,Ajaxアプリケーションでやり取りするのは,このような単純なデータばかりではありません。例えば,名前と住所,電話番号のような複合的な情報(構造データ)をやり取りしたい,というケースも多くあるはずです。
 このような構造データをクライアント/サーバー間で交換するにはいくつかの方法がありますが,最も一般的なのはXML(eXtensibleMarkupLanguage)形式を利用する方法でしょう。XMLでは,HTMLにもよく似たタグの形式で構造化されたデータを記述することができます。例えば,リスト1はXMLで記述された住所データの例です。
リスト1●XML形式で記述された住所データの例<?xml version="1.0" ?>
<data>
  <name>山田祥寛</name>
  <address>千葉県小金井市静波0-0-00</address>
  <tel>000-000-0000</tel>
</data>
 XML自体はなんの変哲もないテキスト形式ですので,使用しているプラットフォームに依存しないのが特徴です。XMLはすでにインターネット上の標準的なデータ交換言語として,RSS配信やWebサービスなどにおいて日常的に利用されています。XMLそのものについては本稿のテーマを外れますので,本サイトの別連載「動かして覚えるXML入門」,拙著「10日でおぼえるXML入門教室」(翔泳社)などを参照してください。
「Yahoo! ウェブ検索Webサービス」とは? 今回紹介するサンプル・アプリケーションは,「Yahoo! ウェブ検索Webサービス」をAjaxアプリケーションから利用する例です(図1)。
 「Yahoo! ウェブ検索Webサービス」はYahoo!が提供しているサービスの一つで,これを利用することで,Yahoo!が提供する検索エンジンをあたかも自前のアプリケーション上で動作しているかのように動作することが可能になります(図2)。
 Yahoo! ウェブ検索Webサービスのしくみは単純です。あらかじめ決められたURLに対して検索に必要なパラメータ()を引き渡すだけです。指定可能なパラメータは多く用意されていますが,最低限の検索機能を実装するだけならば,まずは「appid(アプリケーションID)」と「query(検索文字列)」だけを指定しておけば問題ありません。アプリケーションIDとは,Yahoo!ウェブ検索Webサービスを利用するアプリケーションを識別するためのキーとなるものです。「Yahoo! デベロッパーネットワーク」から登録できますので,あらかじめ手続きを済ませておきましょう。
 まずは,ブラウザ上からYahoo!ウェブ検索Webサービスの動作を確認してみましょう。ブラウザから以下のようなURLでサービスにアクセスします。ここでは「山田祥寛」というキーワードで検索しているわけです。繰り返しですが,appidパラメータの部分は,適宜,自分で取得したアプリケーションIDに置き換えてください。
  http://api.search.yahoo.co.jp/WebSearchService/V1/
         webSearch?appid=wings-project&query=山田祥寛
 検索キーワードでヒットした結果が,以下のようにXML形式で出力されれば成功です。結果XMLに含まれるタグの意味については,「Yahoo!デベロッパーネットワーク - ウェブ検索」のページを参照してください。
<?xml version="1.0" encoding="UTF-8" ?>
  <ResultSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="urn:yahoo:jp:srch"
    xsi:schemaLocation="urn:yahoo:jp:srch http://api.search.yahoo.co.jp/
        WebSearchService/V1/WebSearchResponse.xsd"
    totalResultsAvailable="32200" totalResultsReturned="10"
    firstResultPosition="1">
    <Result>
      <Title>サーバサイド技術の学び舎 - WINGS</Title>
      <Summary>サーバサイド技術(JSP/サーブレット,ASP.NET...</Summary>
      <Url>http://www.wings.msn.to/&lt;/Url>
      <ClickUrl>http://wrs.search.yahoo.co.jp/S=96857362/...&lt;/ClickUrl>
      <ModificationDate>1148655600</ModificationDate>
      <MimeType>text/html</MimeType>
      <Cache>
        <Url>http://wrs.search.yahoo.co.jp/S=96857362/...&lt;/Url>
        <Size>23586</Size>
      </Cache>
    </Result>
  ~ 略 ~
  </ResultSet>
サーバーサイドからYahoo! ウェブ検索Webサービスを利用してみよう ブラウザからアクセスした結果を確認できたところで,次に同様のしくみをPHP(PHP:Hypertext Preprocessor)アプリケーションで作成してみましょう(リスト2)。
リスト2●Yahoo! ウェブ検索WebサービスにアクセスするPHPプログラム(yahoo.php)<?php
// 出力/内部文字コードをUTF-8に設定
mb_http_output('UTF-8');
mb_internal_encoding('UTF-8');
header('Content-Type: text/xml;charset=UTF-8');
$url ='http://api.search.yahoo.co.jp/WebSearchService/V1/webSearch?';
$url.='appid=wings-project&query='
      .urlencode(mb_convert_encoding($_GET['keyword'],'UTF-8','auto'))
      .'&'.'result=15';
print(file_get_contents($url));
?>
 例によって,PHPの細かな構文についての説明は省略します。ここでは入力された値(検索キーワード)をもとに問い合わせURLを作成し,その結果を取得しているとだけ理解しておけば良いでしょう。file_get_contents関数は,指定されたURL($url)にアクセスした結果を文字列として取得するための命令です。
 以下のようなURLでサーバーサイドにアクセスし,先ほどと同様の結果が得られれば成功です。サンプルの実行結果はオンラインで確認できますので,実際にブラウザ上からアクセスして試してみることにしましょう。
  http://www.wings.msn.to/ajax/yahoo.php?keyword=山田祥寛
 いかがですか?先ほどと同様,指定したキーワードによる検索結果がXML形式で表示されれば成功です。今回は,サーバーサイドの解説でAjaxとはやや関係がない内容になってしまいましたが,次回はいよいよ,サーバーサイドで得た結果をクライアントサイドのAjaxで処理する方法についてご紹介します。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-5-25 09:55:46 | 显示全部楼层
AjaxでYahoo! ウェブ検索Webサービスを利用してみよう(クライアントサイド編)


 前回はサーバーサイドでYahoo! ウェブ検索Webサービスにアクセスし,指定キーワードによる検索結果を取得するまでを見ました。今回は,サーバーサイドで得た結果をクライアントサイドで受け取り,ブラウザ上に表示してみることにしましょう。
クライアントサイドで処理結果を受け取ろう まずは,クライアントサイドでサーバーサイドでの処理結果を受け取るコードを見てみます(リスト1)。
リスト1●クライアントサイドのコード(yahoo.html)<html>
<head>
<meta http-equiv="Content-Type" c>
<title>Yahoo! ウェブ検索Webサービス</title>
<script language="JavaScript">
<!--
// [送信]ボタンをクリック時の処理を定義
function search() {
   // 非同期通信を行うためのXMLHttpRequestオブジェクトを生成
  try {
    xmlReq = new ActiveXObject("Microsoft.XMLHTTP");
  } catch(e) {
    xmlReq = new XMLHttpRequest();
  }
   // サーバーからの応答時の処理を定義(結果をページへ反映)
  xmlReq.onreadystatechange = function() {
    var msg = document.getElementById("result");
    if (xmlReq.readyState == 4) {
      if (xmlReq.status == 200) {
        var ctt="";
       var xmldoc=xmlReq.responseXML.documentElement;
       var nodes=xmldoc.childNodes;
       if(nodes.length==0){
         ctt="お探しのサイトは見つかりませんでした。";
       } else {
         for (i =0; i < nodes.length; i++) {
           var node=nodes.item(i);
           ctt += "<li><a target='_blank' href='"
               + getNodeValue(node, "Url")
               + "'>" + getNodeValue(node, "Title")
               + "</a></li>"
         }
       }
       msg.innerHTML=ctt;

      } else {
        msg.innerHTML="通信に失敗しました。";
      }
    } else {
      msg.innerHTML="通信中…";
    }
  }
   // サーバーとの通信を開始
  xmlReq.open("GET","yahoo.php?keyword="
              + encodeURI(document.fm.keyword.value), true);
  xmlReq.send(null);
}

// ノードcurrent配下に含まれる要素nameのテキスト値を取得する関数
function getNodeValue(current ,name){
  var nodes=current.getElementsByTagName(name);
  var node=nodes.item(0);
  var txtNode=node.firstChild;
  return txtNode.nodeValue;
}
-->
</script>
</head>
<body>
<form name="fm">
  キーワード:
  <input type="text" name="keyword" size="15" maxlength="30" />
  <input type="button" value="検索"  />
  <hr />
  <ol type="1">
    <div id="result" />
  </ol>
</form>
</body>
</html>
 やや長いコードにも思われるかもしれませんが,ポイントとなるのはコード内太字の部分です。ほかの個所は,Ajaxプログラミングにおける定型的な処理そのままですので,忘れてしまったという方は,もう一度,連載第2~4回の解説を見直してみましょう。
 サーバーから送信されたXMLデータを取得するのは,XMLHttpRequestオブジェクトのresponseXMLプロパティの役割です。以前のサンプルで使用したresponseTextプロパティがサーバーから送信されたデータを文字列として取得したのに対し,responseXMLプロパティを使用することで応答データをDOMDocumentオブジェクトとして取得できます。
 DOMDocumentオブジェクトはXMLデータ(文書)を操作するためのオブジェクトです。XMLデータを読み込んだり操作したりするには,まずこのDOMDocumentオブジェクトを取得する必要があります。XMLデータ読み込みの詳細については次回に改めて解説しますので,ここではまず,図1でコードの概略の流れをイメージしてみることにしましょう。図と合わせて,コードの流れを追ってみてください。
 このようにXMLデータを読み込む場合には,タグの階層構造をツリーのように見立てて,最上位の要素(ルート要素)から順に下位要素へ向かってアクセスしていくのが一般的です。
 最終的に,結果XMLから<Title>要素(ページタイトル)と<Url>要素(URL)とを取り出し,リンク一覧として整形したものをinnerHTMLプロパティを使って<div>タグに反映させれば完了です。<div>タグに出力される最終結果の例を以下に挙げておきます(見やすいように適宜,改行とインデントを付与しています)。

  <li>
    <a target='_blank' href='http://www.wings.msn.to/'>
      サーバサイド技術の学び舎 - WINGS</a>
  </li>
  <li>
    <a target='_blank' href='http://www.web-deli.com/'>WebDeli - ホーム</a>
  </li>
 いかがですか? 以上の内容を理解できたら,実際に以下のURLからサンプル・アプリケーションを実行してみましょう。連載第5回冒頭に示したような結果を得られれば成功です。

  http://www.wings.msn.to/Ajax/yahoo.html 今回はまず,コード全体を大ざっぱな枠組みから眺めてみました。すべてがすべてを理解できなくても構いません。まずは全体的な流れとイメージをつかみ取ることができればOKです。次回は引き続き本サンプルのXMLデータ読み込みの部分を詳述していきます。
特別コラム●Ajaxにおけるクロスドメイン問題 前回/今回のサンプルを見て,あるいは,次のように思われた方もいたかもしれません。「サーバーサイドの役割って,ただ検索サービスにアクセスした結果をそのまま出力しているだけだよね。それなら,クライアントサイドでXMLHttpRequestオブジェクトで直接アクセスしてもいいんじゃないの?」
 良い思いつきのような気がします。しかし,残念ながら,これは不可です。というのも,本連載3回でも紹介したように,XMLHttpRequestオブジェクトではセキュリティの制限上,デフォルトで「外部サイトとの通信は禁止」されているからです。
 もちろん,ブラウザのセキュリティ制限を引き下げれば,外部サイトとの通信を実現することも可能ではあります。しかし,不特定多数のユーザーが使用するアプリケーションの場合,セキュリティ制限の引き下げを前提とするのは現実的ではないでしょう。
 そこで今回の例のように,外部サイトで公開されているサービスにアクセスする必要がある場合には,サービスにアクセスするための「サーバーサイド」のアプリケーション(「プロキシ」とも言います)を作成しておき,これに対して,クライアントサイドからアクセスするという方法を採る必要があるわけです。これによって,Ajaxアプリケーションから外部サービスを利用することが可能になります。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-5-25 09:58:03 | 显示全部楼层
Ajax活用のためのDocument Object Model入門


 前回,前々回と,Yahoo!ウェブ検索Webサービスを利用するサンプルを通じて,AjaxでXMLデータを利用する方法について紹介しました。XMLを利用することで,サイト検索結果のような複合的な情報(構造化データ)を,使用するプラットフォームを意識することなく,やり取りできます。前回,XMLデータをクライアントサイドで受け取れることを確認したところで,今回はより詳らかにXMLデータ読み込みの部分を見ていくことにしましょう。
XML操作の標準API「Document Object Model」 DOM(Document ObjectModel)とは,その名の通り,XML文書内に登場するタグや属性,テキストといった構成要素を汎用的に操作するためのオブジェクト群のことを言います。DOMを利用することで,与えられたXML文書から必要な情報を取り出したり,データを編集/追加/削除したり,といった操作が可能になります。前回登場したDOMDocumentオブジェクト(XML文書)も,DOMが提供するオブジェクトの一つです。
 DOMを利用するうえでは,まず「ノード(node)」という言葉を理解しておく必要があります。ノードとは,XML文書を構成する最小の単位だと思っておけばよいでしょう。XML文書内に登場するタグ(要素),属性,テキスト,コメント…などすべての構成要素は,ノードと見なされます。DOMでは最初にXML文書全体を読み込み,ノード間の階層構造をツリー構造としてメモリー上に展開します。例えば,前回,前々回と説明したYahoo!ウェブ検索Webサービスから返された結果XMLは,内部的には以下のようなツリー構造として展開されます(図1)。
 これによって,文書ツリー上の特定のノード(情報)を取り出したり,あるいは,文書ツリーの一部分(部分ツリー)を置き換え/削除したり,はたまた,文書ツリーに新たな情報を追加したり,といった柔軟な操作が可能になるわけです。DOMは実に膨大な仕様を提供しており,そのすべてを紹介することはできません。本稿ではAjaxプログラミングで頻繁に利用すると思われる参照系の機能を,前回紹介したコードに沿って見ていきます。より詳細については,拙著「10日でおぼえるXML入門教室」(翔泳社)や「基礎XML」(インプレス)などの専門書を参照してください。
ルート要素を取得する 以下に,前回のクライアントサイドのプログラムからXMLデータを処理する部分を再掲します(リスト1)。このコードでXMLの各ノード・データをどのように読み取っているのかを見ていきましょう。
リスト1●クライアントサイドでXML文書の処理をしている部分のJavaScriptのコード(yahoo.htmlの一部) ~ 略 ~
   // サーバーからの応答時の処理を定義(結果をページへ反映)
  xmlReq.onreadystatechange = function() {
    var msg = document.getElementById("result");
    if (xmlReq.readyState == 4) {
      if (xmlReq.status == 200) {
        var ctt="";
        var xmldoc=xmlReq.responseXML.documentElement;  // … (1)
        var nodes=xmldoc.childNodes;                    // … (2)
        if(nodes.length==0){                            // … (3)
          ctt="お探しのサイトは見つかりませんでした。";
        } else {
          for (i =0; i < nodes.length; i++) {
            var node=nodes.item(i);                     // … (4)
            ctt += "<li><a target='_blank' href='"      // … (5)
                + getNodeValue(node, "Url") + "'>"
                + getNodeValue(node, "Title") + "</a></li>"
          }
       }
       msg.innerHTML=ctt;
 ~ 略 ~
    }
  }
 ~ 略 ~
// ノードcurrent配下に含まれる要素nameのテキスト値を取得する関数
function getNodeValue(current ,name){
  var nodes=current.getElementsByTagName(name);
  var node=nodes.item(0);
  var txtNode=node.firstChild;
  return txtNode.nodeValue;
}
 ~ 略 ~
 XML文書の読み取りにはいくつかのアプローチがあります。その代表的なアプローチが「ノードウォーキング」と呼ばれるものです。ノードウォーキングとは,ツリー構造になったXML文書の最上位ノードから下位ノードに向かって順に辿っていく方法のことを言います。ノードウォーキングを行う場合の操作の基点となるのが「ルート要素」です。
 ルート要素とは,その名の通り,文書ツリーの根っこ??ツリーのように広がった文書階層を束ねる最上位の要素のことを言います。図1では<ResultSet>要素のことです。ルート要素を取得するには,documentElementプロパティを使用します。リスト1では(1)の部分のコードが該当します。
  var xmldoc=xmlReq.responseXML.documentElement;
子ノード群を取得する 図1を見てもわかるように,ルート要素は文書内の全ノードを含んでいます。下位ノードに辿っていく次のステップとして,今度はルート要素直下の子ノード群を取得してみましょう。リスト1では,(2)の部分に当たります。
  var nodes=xmldoc.childNodes;
 childNodesプロパティは,現在のノード(この場合はルート要素)配下の子ノード群をDOMNodeListオブジェクトとして返します*1。DOMNodeListオブジェクトは,0個以上のDOMNodeオブジェクト(ノード)を含むリストで,図1の場合は,<ResultSet>要素配下の一連の%lt;Result>要素群を表します。リスト1の(3)では,DOMNodeList.lengthプロパティでリスト内のノード数を参照し,0である場合には結果がなかったことをメッセージ表示し,1以上であった場合にだけ個々のノードを取得する処理を行います。

  if(nodes.length==0){
    ctt="お探しのサイトは見つかりませんでした。";
  } else {
    /* 個々のノードを取得する処理 */
  }
個々のノードを取得する DOMNodeListオブジェクトから個々のノードを取得しているのは,リスト1の(4)のコードです。

  for (i =0; i < nodes.length; i++) {
    var node=nodes.item(i);
    ~ 略 ~
  }
 itemプロパティは引数に指定されたインデックス番号に対応するノードをDOMNodeオブジェクトとして返します。つまり,このforループでは「0 ~ノード数?1」番目まで繰り返すことで,リストに含まれるすべてのノードを一つずつ取り出しているというわけです(リストのインデックス番号は0がスタート点であることに注意してください)。
要素ノードにダイレクトアクセスする ここまでの操作で,ルート要素配下の個々のノード(ここでは<Result>要素)が取得できました。ここからさらに配下の<Title>,<Url>要素にアクセスします。そのための方法としてchildNodesプロパティを使用しても(もちろん)構いませんが,ここではノードウォーキングと並んでよく使われる「ダイレクトアクセス」によって,以下のノードにアクセスしてみましょう。
 ノードウォーキングが上位のノードから順に下位のノードへたどっていく手法であるのに対して,ダイレクトアクセスは指定された要素名やID値などをキーとして該当するノードに直接アクセスします。手軽である半面,対象の文書ツリーが大きい場合にはパフォーマンスが悪化する原因にもなりますので,注意が必要です。
 一般的には,今回の例のように,まずノードウォーキングで対象のツリーを絞り込んだうえで,個々のノードへのアクセスはダイレクトアクセスを用いるなど,両者を併用するのが好ましいでしょう。
 リスト1でダイレクトアクセスを行っているのは,(5)の部分です。

  ctt += "<li><a target='_blank' href='" + getNodeValue(node, "Url") + "'>"
      + getNodeValue(node, "Title") + "</a></li>";
 同じようなコードを何度も記述するのは好ましくありませんので,本稿ではダイレクトアクセスの部分をgetNodeValueというユーザー定義関数として別個に定義しています。getNodeValue関数は,引数として検索対象のノード(DOMNodeオブジェクト)と取得したい要素の名前を受け取り,戻り値として指定要素配下のテキストを返します。なお,getNodeValue関数は,対象ノードの配下に同名の要素が複数あった場合には,最初の要素に含まれるテキストのみを返すものとします。

  function getNodeValue(current ,name){
    var nodes=current.getElementsByTagName(name);
    var node=nodes.item(0);
    var txtNode=node.firstChild;
    return txtNode.nodeValue;
  }
 getElementsByTagNameメソッドは現在のノード(current)配下を指定されたタグ名(name)で検索し,合致した要素群をDOMNodeListオブジェクトとして返します。ここでは常に合致する要素が一つしかないことを前提としますので,DOMNodeListオブジェクトから0番目のノードを取得しておきます。これで,(例えば)引数に指定された<Url>,<Title>要素を取得できます。
テキスト・ノードを取得する DOMを利用する場合,間違えやすい点の一つとして,要素配下のテキストは「要素ノードの値ではなく,要素ノード配下のテキスト・ノードの値である」という点が挙げられます。つまり,要素配下のテキストを取得するには,まず先ほど取得した要素ノードからテキスト・ノードを取り出す必要があります。getNodeValue関数の以下の部分に注目してみましょう。

  var txtNode=node.firstChild;
  return txtNode.nodeValue;
 要素配下のテキスト・ノードを取得する場合,先ほどのchildNodesプロパティを使用しても構いませんが,それではコードが無用に冗長になってしまいます。配下にノードが一つしかないことがあらかじめわかっている場合には,firstChildプロパティを利用すると便利です。
 firstChildプロパティを利用すると,現在のノード配下の最初の子ノードを取得できます。ちなみに,firstChildプロパティ同様,DOMには相対的な位置関係でノードを取得するための様々なプロパティが用意されています。図2に主要なものをまとめておきます。
 これでテキスト・ノードが取得できました。あとはnodeValueプロパティでテキスト・ノードの値を取得するだけです。ちなみに,ノードの情報を取得するプロパティにはnodeValueプロパティのほかにも,表1のようなものがあります。
[tr]プロパティ概要[/tr]
表1●ノード情報取得のための主要なプロパティ(DOMNodeオブジェクト)
nodeNameノード名
nodeValueノード値
prefix名前空間プレフィックス
namespaceURI名前空間URI
nodeNameノード名
 以上,DOMによるXML文書読み取りの基本的な手法について学習してきました。繰り返しですが,DOMそのものは実に膨大な仕様を提供しており,とてもこの場だけですべてを語りつくせるものではありません。本稿では紹介できなかった重要な機能もまだまだありますので,余力のある方は,前述の参考書籍なども合わせて参照することをお勧めします。

*1 ちなみに子ノードには属性は含まれません。要素に含まれる属性値にアクセスするには,以下のようなコードを記述してください。
  xmldoc.getAttribute("totalResultsReturned")
これによって,現在の要素(この場合は<ResultSet>要素)配下のtotalResultsReturned属性の値を取得できます。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-5-25 09:59:12 | 显示全部楼层
Ajax対応ライブラリを利用しよう


 前回紹介したように,XML(eXtensible MarkupLanguage)を利用することにより,検索エンジンの検索結果のようにサイト名,URL,サマリ文などの情報を含む複合的な情報(構造化データ)を容易に受け渡しできるようになります。しかし,ここまでに見てきた方法にはいくつかの問題があります。
(1)クロスブラウザ問題
 Ajax技術を構成する中核の要素がクライアントサイド・スクリプトである以上,避けて通れないのが「クロスブラウザ問題」です。クロスブラウザ問題とは,ブラウザ間の仕様差によって発生する挙動の違いのことを言います。連載第3回でも紹介したXMLHttpRequestオブジェクトの違いを代表として,クライアントサイドでコードを記述する場合には常にクロスブラウザ問題を意識してコーディングする必要があります。そのため,ときとしてトリッキーなコーディングを要求される場合もあり,クライアントサイドの開発生産性を低下する一因になっています。
(2)DOMによるコードは冗長になりやすい
 なるほど,DOM(Document ObjectModel)はXML文書処理のための強力なAPIです。しかし,連載第6回のyahoo.htmlを見てもわかるように,DOMによるコードは往々にして回りくどく,シンプルな処理を記述するにも冗長になりやすいという難点があります*1。また,出力レイアウトをスクリプト内で動的に生成していますので,複雑なレイアウトになればなるほどコードがわかりにくくなり,レイアウトの変更に即座に対応しにくい,という問題もあります。
Ajax対応ライブラリを利用しよう そこで本連載の最後のテーマとして,Ajax対応ライブラリについて取り上げます。Ajax対応ライブラリとは,その名の通り,Ajaxプログラミングを効率化するクラスライブラリやフレームワークのことです。標準の機能だけで一からAjaxアプリケーションを構築するのも良いかもしれませんが,これらライブラリを利用することで,より効率的にアプリケーションを構築できるようになります。
 Ajax対応ライブラリと一口に言っても様々な種類がありますが,以下に比較的目にすることの多いライブラリをプラットフォームごとに分類してみました(表1)。
[tr]分類名称概要[/tr]
表1●プラットフォーム別のAjax対応ライブラリ
ASP .NETAtlas FrameworkAjax機能をASP .NETに実装するためのJavaScriptライブラリとHTTPハンドラのセット
Ajax .NETASP .NET上でAjaxによるサーバー・メソッド呼び出しを可能にするHTTPハンドラ
JavaAjaxTagsJSP(JavaServer Pages)上で利用可能なAjax対応タグライブラリ。特別なフレームワークに依存せず,サーバーサイドJava環境全般で利用可能
AjaxFacesJSF(JavaServer Faces)上で利用可能なAjax対応UIコンポーネント。JSFとの親和性に優れる(正式版の利用は有償)
DWR(Direct Web Remoting)JavaScriptとJavaの間を繋ぐRMI(Remote Method Invocation)。特定フレームワークに依存しないため,既存システムへの導入にも有利
JavaScriptJKL.Hina+JKL.ParseXMLXMLHttpRequestによる通信とJavaScriptテンプレート・エンジン機能を提供。ブラウザ間の差異を吸収するほか,XML解析/出力のコードを簡素化し,保守性を向上する
Prototype.jsXMLHttpRequestのラッピングやJavaScriptによる開発全般のフレームワーク
Ajax PagesHTML埋め込み型の構文を提供するJavaScriptテンプレート・エンジン
PHPPEAR::HTML_AJAXPHP上でAjaxアプリケーションを動作させるためのシンプルなフレームワーク
AJSONJASON形式でクライアント-サーバー間のデータを交換する
RubyRuby On RailsRuby上で動作するMVCデザインのアプリケーション・フレームワーク
 例えば,オートコンプリート,タブペイン,ツリービューといった特定の機能をAjaxで実現したいのであれば,AjaxTagsやAjaxFacesのような目的特化型のライブラリを利用すると良いでしょう。定型的な機能を実現するために,自らコードを記述する必要はありません。あらかじめ用意された部品を利用することで,そもそもAjaxをほとんど意識することなく,Ajax技術を導入できます。
 クロスブラウザ問題を解決したい,データ交換にかかわるコーディングを簡素化したい,など,先に挙げたようなAjax開発上の問題を解決したいのであれば,JKL.Hina+JKL.ParseXMLやAjaxPagesのようなJavaScriptライブラリを導入するのが良いでしょう。サーバーサイドのライブラリと異なり,導入の手間を最小限にして,問題を比較的手軽に解決できます。サーバーサイド技術に習熟しているのであれば,DWR,AtlasFramework,PEAR::HTML_AJAXのようなライブラリを導入することで,既存のプラットフォームに容易にAjax機能を追加することが可能です。
JKL.Hina+JKL.ParseXMLを使ってみよう 本稿では,これらのライブラリの中でも,JavaScriptベースで作られており,導入が容易なJKL.Hina+JKL.ParseXMLライブラリを利用してみます。JKL.ParseXMLはクライアントとサーバーの間の通信手続きを簡素化し,サーバー側から受け取ったXML文書を手軽に処理するためのライブラリです。JKL.Hinaは与えられたデータとテンプレート(出力レイアウトの雛形)とを結びつけるためのテンプレート・エンジン(図1)です。
 これらのライブラリを利用することにより,
  • XMLHttpRequestオブジェクトによる通信を意識する必要がない
  • 複雑なXMLデータにもより直感的にアクセスできる
  • レイアウトをロジック部分から切り離せるのでデザインの保守性が向上する
などのメリットを享受できます。川崎有亮氏によって開発が進められており,日本語ドキュメントが充実しているのも初学者にはうれしいポイントでしょう。 JKL.Hina+JKL.ParseXMLライブラリは,先の表で示したURLからダウンロードできます。導入はカンタン。ダウンロードしたjkl-hina.js,jkl-parsexml.jsをアプリケーションを配置したフォルダに配置するだけです。これでJKL.Hina+JKL.ParseXMLライブラリを利用できるようになります。
 さて,今回はここまでです。連載の最終回である次回は,JKL.Hina+JKL.ParseXMLライブラリを利用して,連載の第6回で作成したyahoo.htmlを書き換えてみます。

*1 このような問題を解決するために,昨今ではYAML(YAML Ain’t MarkupLanguage)やJSON(JavaScript ObjectNotation)のようなテキストフォーマットを用いてデータ交換を行う方法も提供されています。Ajax(AsynchronousJavaScript And「Xml」)だからといって,XMLにこだわる必要はありません。本稿ではこれらフォーマットについては省略しますが,これらフォーマットを利用することにより,コードをシンプルに記述できる場合があります。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-5-25 10:00:56 | 显示全部楼层
JavaScriptライブラリでAjaxプログラミングを効率化しよう


 前回,Ajaxプログラミングを効率化する手段として,ライブラリやフレームワークの導入について述べました。様々な環境に対応したAjaxライブラリ/フレームワークが数あまた提供されていることがおわかりになったと思います。最終回である今回は,その中でも比較的手軽に導入することができるJKL.Hina+JKL.ParseXMLライブラリを使って,本連載の第6回で紹介したyahoo.htmlを改良してみましょう。
JKL.Hina+JKL.ParseXMLライブラリを使ってみよう 前回,すでにライブラリの概要については紹介してありますので,さっそくyahoo.htmlを書き換えてみることにしましょう(リスト1)。
リスト1●JKL.Hina+JKL.ParseXMLライブラリを使ったコード(yahoo_jkl.html)<html>
<head>
<meta http-equiv="Content-Type" c>
<!--必要なライブラリをあらかじめロード-->
<script language="JavaScript" src="jkl-parsexml.js"></script>
<script language="JavaScript" src="jkl-hina.js"></script>
<title>Yahoo! ウェブ検索Webサービス</title>
<script language="JavaScript">
<!--
// [検索]ボタンをクリック時の処理を定義
function search() {
  result.innerHTML="通信中…";
   // サーバーからの応答時の処理を定義(結果のテンプレートへの反映)
  var callback = function(data){
    var hina=new JKL.Hina("template");
    template.style.display="block";
    hina.expand(data['ResultSet'],"result");
    template.style.display="none";
  };
  var jkl = new JKL.ParseXML("yahoo.php?keyword="
    + encodeURI(document.fm.keyword.value));  // 送信先URLの指定
  jkl.async(callback);  // 非同期通信の設定
  jkl.parse();          // リクエスト開始
}
-->
</script>
</head>
<body >
<form name="fm">
  キーワード:
  <input type="text" name="keyword" size="15" maxlength="30" />
  <input type="button" value="検索"  />
  <hr />
  <!--出力テンプレートの定義-->
  <div id="template">
    <div title="@foreach info [/Result]">
      <li>
        <a _href="[/info/Url]" target="_blank">[/info/Title]</a>
      </li>
    </div>
  </div>
  <!--出力先のプレイスホルダ(置き場所)-->
  <ol type="1">
    <div id="result" />
  </ol>
</form>
</body>
</html>
 コードの大まかな流れについては,リスト内のコメントや前回までの解説を参照いただくとして,ここではポイントとなる3点にフォーカスしてみましょう。
(1)JKL.ParseXMLライブラリで非同期通信を行う
 JKL.ParseXMLライブラリで非同期通信を行っているのは,以下の部分です。
  var jkl = new JKL.ParseXML("yahoo.php?keyword="
               + encodeURI(document.fm.keyword.value));
  jkl.async(callback);
  jkl.parse();
JKL.ParseXMLライブラリの使い方はカンタン。JKL.ParseXMLライブラリのコンストラクタにXMLデータの取得先(ここではXMLデータを生成するPHPスクリプト)を指定した後,asyncメソッドでコールバック関数を指定するだけです*1。あとは,parseメソッドを呼び出すだけでXMLデータを取得できます。元のコードにあったXMLHttpRequestオブジェクトによる通信の手続きが一切不要であることがおわかりになるでしょう。
 取得したXMLデータは配列形式に変換されたうえで,コールバック関数(ここではcallback関数)の引数として渡されます。変換済みの配列にアクセスするには,例えば,以下のように記述します。
  data['ResultSet']['Result'][0]['Title']
これによって,「<ResultSet>要素配下の1番目の<Result>要素の<Title>要素」の値を取得できます。DOMによってノードウォーキングするのに比べ,はるかに直感的なコードで各要素にアクセスできることがおわかりになるはずです。
(2)JKL.Hinaクラスでテンプレートを記述する
 サーバーからデータを取得できたところで,いよいよJKL.Hinaライブラリの登場です。あらかじめ用意したテンプレートに対して必要なデータを流し込んでみましょう。
 まずはレイアウトを規定するテンプレートを用意する必要があります。JKL.Hinaライブラリで利用可能なテンプレートは,
タグに記述します。本サンプルでは,以下の部分です。  &ltdiv id="template">
    &ltdiv title="@foreach info [/Result]">
      &ltli>
        &lta _href="[/info/Url]" target="_blank">[/info/Title]</a>
      </li>
    </div>
  </div>
テンプレートに変数を埋め込むには,「[/変数名]」の形式で指定します。ただし,属性値として変数を埋め込む場合には,対応する属性名の先頭にアンダースコア(_)を付与しなければならない点に注意してください。
 配列に含まれるデータを順に取り出すのは,@foreach命令の役割です。JKL.Hinaライブラリでは,テンプレート内で条件分岐や繰り返し処理のようなごく基本的な制御構文にも対応しているのが特徴です。以下に@foreach命令の一般的な構文を示します。
  @foreach 取り出したデータを格納する一時変数 [元となる配列データ]
@foreachのような命令はtitle属性の値として記述する必要があります。取り出した一時変数には,<Result>要素が含まれているはずです。ループ内で<Result>要素配下の要素にアクセスするには,スラッシュ(/)区切りで「[/info/Url]」のように記述してください。
 なお,テンプレートはページロード時に非表示(displayプロパティを"none"に設定)にしておく必要があります。さもないと,未解析のテンプレートがそのままページ上に表示されてしまいますので,注意してください。
(3)JKL.Hinaクラスからテンプレートを利用する
 最後に,(2)で用意したテンプレートに対して,データを流し込んでいるのが,コールバック関数の中の以下の部分です。
  var hina=new JKL.Hina("template");
  template.style.display="block";
  hina.expand(data['ResultSet'],"result");
  template.style.display="none";
JKL.Hinaクラスのコンストラクタには,適用するテンプレートのID値を指定します。テンプレートを使用するにあたっては先ほど非表示にしたテンプレートを表示状態(displayプロパティを"block"に設定)にする必要があります。さもないと,正しくテンプレートが適用されませんので,注意が必要です。
 テンプレートを適用するのは,expandメソッドの役割です。第1引数から引き渡すデータ,適用結果の出力先(ID値)を指定します。テンプレートを使用した後は,テンプレートを元の非表示状態に戻すのを忘れないようにしてください。
 以上を理解したら,yahoo_jkl.htmlを実行してみましょう。yahoo.html同様,入力したキーワードに対応するサイト検索の結果が表示されれば成功です。
まとめ 以上,全9回を通じて,Ajaxプログラミングのごく基本的な事項を学んできました。もっとも,Ajax技術を利用するうえでは知っておかなければならないことはまだまだ多くあります。特に最後に扱ったAjax対応ライブラリなどは種類も利用環境も様々で,興味深いトピックスも豊富なところです。「基本のキ」という本連載のテーマからは外れてしまいますので,次の機会に改めて紹介できればと思っています。本連載が,Ajaxの基本を理解したい皆さんの取っ掛かりとなれば幸いです。
 なお,本連載で紹介したサンプルは,著者サポートサイトの以下のページからオンラインで確認することができます(表1)。お試しください。
[tr]連載の回サンプル(リンクをクリックすると該当ページにジャンプします)[/tr]
表1●オンラインで確認できるサンプル
第2回書籍検索(PHP版)
第2回書籍検索(Ajax版)
第4回書籍検索(Ajax[POST対応版])
第6回Yahoo! ウェブ検索
第9回Yahoo! ウェブ検索(JKL.Hina対応)


*1 JKL.ParseXMLライブラリは同期通信にも対応しています。同期通信を行うには,asyncメソッドを呼び出さずに直接parseメソッドを呼び出します。同期通信によるサーバーサイドからの応答結果はparseメソッドの戻り値として取得できます。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注~册

本版积分规则

小黑屋|手机版|咖啡日语

GMT+8, 2024-5-12 00:21

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表