jQueryではじめるAjax

第2回jQueryによるAjax実装

第1回ではJSONPによるAjax実装を取り上げましたが、今回はそこで使ったJSONという表記法と、JSONPというAjax実装手法について解説します。また、jQueryによる簡単なAjaxの実装についても解説します。

JSONってなに?

JSONとは、JavaScript Object Notationの略称です。ECMAScript言語 ECMA-262をベースに作られた、Javascriptオブジェクトの表記法のサブセットです。

JSONは、名前と値のペア、および、順序付きの値という2つのシンプルな構造に基づいて、構造化データを簡潔に表現することができます。

オブジェクト(名前と値のペアの集まり)

JSONでオブジェクトを表現するには、メンバを「{」「}」で囲みます。複数のメンバは「,」で区切られます。例えば、名前が「color⁠⁠、値が「green」というメンバを持つオブジェクトを表現するには、次のように記述します。

{"color": "green"}

試しに、このオブジェクトを生成し、アクセスしてみましょう。

リスト1
// オブジェクトの生成
var obj = {"color": "green"};

// "color" プロパティにアクセス
alert(obj.color);       // "green"

alert(obj["color"]);    // "green"

配列(順序付きの値の集まり)

JSONで配列を表現するには、値を「[」「]」で囲みます。複数の値は「,」で区切られます。例えば、⁠red」⁠green」⁠blue」という値を持つ配列を表現するには、次のように記述します。

["red", "green", "blue"]

試しに、この配列を生成し、アクセスしてみましょう。

リスト2
// 配列の生成
var array = ["red", "green", "blue"];


// 配列の各要素にアクセス
alert(array[0]);    // "red"
alert(array[1]);    // "green"
alert(array[2]);    // "blue"

構造化

オブジェクト、配列はネストすることができます。例えば、以下のようにしてオブジェクトを表現することができます。

リスト3
// オブジェクトの生成
var obj = {
    "title" : "reunion",
    "users": [
        {
            "name": "aoki",
            "code": "0001",
            "age": 34
        },
        {
            "name": "ueno",
            "code": "0002",
            "age": 33
        }
    ]
};


// 各メンバにアクセス
alert(obj.title);            // "reunion"

alert(obj.users[0].name);    // "aoki"
alert(obj.users[0].code);    // "0001"
alert(obj.users[0].age);     // "34"

alert(obj.users[1].name);    // "ueno"

alert(obj.users[1].code);    // "0002"
alert(obj.users[1].age);     // "33"

表記の制約

名前は文字列でなければなりません。かならず「"」で囲みます。

値は、文字列、数値、オブジェクト、配列、真偽値(true、false)、nullのいずれかです。文字列は、⁠"」で囲まれたUnicode文字の集まりです。数値は、10進数のみ表記できます(整数、浮動小数点⁠⁠。

基本的にこの表記法がJSONです。とてもシンプルですね。詳細は以下を参照してください。

参考

Youtube APIのデータフォーマットにjson-in-script(JSONP)を指定すると、このJSONでデータが返ります。Youtubeからのレスポンスデータがどのような構造になっているか、解析してみてください。

なぜJSONを使うのか

Ajaxの「x」は、XMLの頭文字です。もともとは、戻り値の形式としてXMLが想定されていました。Youtubeのレスポンスデータも、何も指定しないとatom(XML)になります。しかしなぜ、XMLではなくJSONを使うのでしょうか(もちろんXMLを使ってもかまいません⁠⁠。

最大の理由はJavascriptで扱いやすいことです。JSONはJavascriptオブジェクトの表記法のサブセットなので、そのテキストをJavascriptとして評価するだけで扱えるようになります。

現在、JavascriptでXMLを扱う場合は、何かしらのライブラリを介すのが一般的です。ECMAScript for XMLという、ECMAScriptからXMLをネイティブに扱うための言語拡張も仕様化されていますが、対応するWebブラウザはまだ少ないのが現状です。

参考

JSONPってなに?

JSONPとは、JSON with Paddingの略称です。JSONデータを引数としてJavascriptの関数を呼び出す形式で、レスポンスを受け取る方式を指します。

たとえば、関数の名前にmethodと指定すると、以下のような内容がレスポンスとして返ってきます。

method({"color": "green"});

よって、methodという名前の関数が定義されていれば

{"color": "green"}

を引数としてmethod関数が実行されます。

このようにして、レスポンスデータとして、定義した関数をコールバックしてもらいます。

Youtube APIでは、callbackパラメータにコールバック関数の名前を指定します。試しに、Youtube APIに対してデータ形式をJSONP、最大取得件数を10、キーワードをtest、callback関数をviewとしてリクエストを送信し、レスポンスを確認してみましょう。Webブラウザのアドレスに次のように入力し、送信してください。

http://gdata.youtube.com/feeds/api/videos?callback=view&vq=test&max-results=10&alt=json-in-script

Webブラウザに次のようなレスポンスが表示されます。

view({"version":"1.0","encoding":"UTF-8",...});

*詳細は省略しています。

callbackパラメータに指定したviewという関数を呼び出す形でデータが返ってくることが確認できます。

そして、このJSONP形式でレスポンスを返すURLを、scriptタグのsrc属性に指定すれば、Webページの読み込みとは非同期にデータを取得し、処理することができます。 このように、JSONPは実装がとても簡単です。コールバック関数を定義し、リクエストするscriptタグを生成するだけで実装できます。

<script type="text/javascript" src="http://gdata.youtube.com/feeds/api/videos?vq=..." />

なぜJSONPを使うのか

JSONPのほかにAjaxを実装する方法としてXMLHttpRequestオブジェクトを使った実装方法もあります。なぜ、JSONPを使うのでしょうか(もちろんXMLHttpRequestオブジェクトを使ってもかまいません⁠⁠。

それは、XMLHttpRequestオブジェクトを使用した方法では、セキュリティ上の制限により他のドメインのサイトへはアクセスできないからです。 たとえば、このgihyo.jpドメインからyoutube.comドメインへリクエストを送信することはできません。

これでは、他のドメインで提供されているサービスを使ってデータを生成したり、サイトを構築することができなくなります。もちろん、自ドメインのサーバ側に他のドメインへアクセスする仕組みを作り、それを呼び出すようにすれば回避できますが、JSONPにくらべ実装に手間がかかり複雑になります。

scriptタグを利用したJSONPでは、scriptタグにクロスドメインアクセスの制限がないため、クライアント側の実装だけで他ドメインのサービスや機能を簡単に呼び出すことができます(セキュリティリスクをよく考慮して実装してください⁠⁠。

jQueryによるAjax実装

Ajaxに関する基礎的な説明を終えたところで、いよいよjQueryによるAjax実装を解説します。

jQueryってなに?

jQueryとは、John Resigによって開発された Javascriptライブラリです。WebブラウザによるJavascriptの実装の違いを吸収し、AjaxやDOMプログラミングコードを簡潔に書くことができます。

2008年1月現在の最新バージョンは、1.2.1です。バージョン1.2からJSONPがネイティブにサポートされ、ますます使い勝手が良くなりました。

参考

jQueryを使う準備

まず、jQueryを、http://jquery.com/からダウンロードします。jQueryには、Minified(縮小版⁠⁠、Packed(圧縮版⁠⁠、Uncompressed(非圧縮版)があります。本連載では解説のため、可読性が最も高い、非圧縮版(Uncompressed)を使います[1]⁠。

あとは、ソースコードからダウンロードしたjQueryを指定するだけです。

リスト4
<html>
<head>
    <script type="text/javascript" src="jquery.js"></script>

    <script type="text/javascript">
        // jQueryを利用してコードを書く
    </script>
</head>
<body>

    <script type="text/javascript">
        // jQueryを利用してコードを書く
    </script>
</body>
</html>

基本的な使い方

$()でjQueryオブジェクトを生成し、jQueryのメソッドを実行するのが基本的な使い方です。$()の引数には、HTML、DOM、CSSセレクタなど、いろいろな引数を受け入れDOMを走査してくれます。プラグインを使えば、XPathセレクタを使用することもできます。

jQueryオブジェクトの生成例をいくつか挙げてみます。

<p>Hello world!</p> というDOM要素のjQueryオブジェクトを生成します。

$("<p>Hello world!</p>")

id=frmSearchで定義されるDOM要素のjQueryオブジェクトを生成します。idの値を指定してjQueryオブジェクトを生成するには、次のように「#」をつけます。

$("#frmSearch")

p要素の子要素であるstrong要素のjQueryオブジェクトを生成します。

$("p > strong")

p要素の子孫要素のうち、'jQuery' という文字列を含むstrong要素のjQueryオブジェクトを生成します。

$("p strong:contains('jQuery')")

jQueryオブジェクトを生成すれば、あとはjQueryのメソッドを実行するだけです。 たとえば、p要素の子要素であるstrong要素を非表示にするには、hide()メソッドを使用して次のように記述します。

$("p > strong").hide();

jQueryの詳細については、Documentationを参考にしてください。また、拙作サイトのjQuery - StackTraceでは、日本語での情報を提供しています。よろしければご覧ください。jQueryのより詳しい使い方は、サンプル実装の解説とともに適宜おこないます。

実装してみよう(1)

それでは、jQueryによるAjax実装をしてみましょう。 今回は、第1回で実装した機能と同じものを、jQueryを使って実装して見たいと思います。

機能としては、次のものを実装します。

  • テキストボックスに文字列を入力して「検索」ボタンを押すと、検索結果のサムネイル画像を10件表示する
  • サムネイル画像をクリックすると、ビデオの再生ページに遷移する

jQueryの読み込み

まずは、Javascriptコードの先頭でjQueryを読み込みます。ソースコードと同じディレクトリに配置したとすれば、次のように指定になります。

リスト5
<script type="text/javascript" src="jquery-1.2.1.js"></script>

キーワード入力、検索結果表示部分

検索キーワードを入力するためのテキストボックスを作ります。また、検索を実行する「検索」ボタンを作りますリスト6⁠。

リスト6
<form id="frmSearch">
  <input type="text" id="keyword">
  <input type="submit" value="検索">

</form>
<div id="videos"></div>

id=frmSearchとしてform要素を生成します。検索を実行する「検索」ボタン、また、検索結果の表示領域としてid=videosで定義されるdiv要素を生成します。

第1回では、formタグに、submit時のイベントハンドラを定義しましたが、今回はjQueryを使ってイベント処理を局所化するため、ここには記述しません。

初期化処理

jQueryでページ読み込み時に初期化処理を行うには次のように定義します。

$(function(){
    // 初期化処理...
})

このメソッドはページが読み込まれ、DOM要素の準備ができた段階で画像などの読み込みを待たずに、即座に実行されます。初期化処理、イベントハンドラなどを実装するときに使用します。ここでは、検索フォームのsubmitイベントハンドラを定義します。

リスト7
$(function(){
    // submitイベントハンドラ
    $("#frmSearch").submit(function(){
        search($("#keyword").val());
        return false;
    });
});

submitイベントハンドラ

id=frmSearchで定義される検索フォームのjQueryオブジェクトを生成します。

$("#frmSearch")

そのフォームオブジェクトに対してjQueryのsubmit(fn)メソッドを呼び出すと、引数のfnをコールバック関数としてsubmitイベントハンドラを定義することができます。

$("#frmSearch").submit(function(){
    // イベント処理
});

ここでは、入力されたキーワードを取得し引数として、searchメソッドを呼び出します。id=keywordで定義されるテキストボックスの値を取得するには、次のようにjQueryのval()メソッドを呼び出します。

$("#keyword").val()

この値を引数としてsearchメソッドを呼び出します。

search($("#keyword").val());

また、このform要素がリクエストを送るわけではないので、searchメソッドを実行後、submitイベントハンドラにfalseを返し、action処理をキャンセルします。

return false;

実装してみよう(2)

検索

キーワードを引数として検索を実行する関数を実装します。データを解析し、プレーヤのページへリンクを張ったサムネイル画像を表示します。

リスト8
function search(keyword) {
    // (1) 表示領域を初期化します。
    $("#videos").text("Loading...");

    // (2) ajax通信を行います
    $.ajax({
        dataType: "jsonp",    // (3) データ形式はJSONPを指定します。
        data: {               // (4) リクエストパラメータを定義します。
            "vq": keyword,
            "max-results":"10",
            "alt":"json-in-script"
        },
        cache: true,          // (5) キャッシュを使用します。
        url: "http://gdata.youtube.com/feeds/api/videos",
        success: function (data) {  // (6) データ取得に成功した場合の処理を定義します。

            $("#videos").empty();
            $.each(data.feed.entry, function(i,item){    // (7) entryの各要素へアクセスします。
                var group = item.media$group;

                $("<a/>")                                                         // (8) a要素を生成
                    .attr("href", group.media$player[0].url)                      // (9) a要素のhref属性を設定
                    .append("<img src='" + group.media$thumbnail[0].url + "'/>")  // (10) a要素の子要素にimg要素を追加 
                    .appendTo("#videos");                                         // (11) a要素を表示領域の子要素に追加
            });
        }
    });
}

まず、表示領域をクリアしますリスト8-(1)⁠。

ビデオを検索するajax通信を定義しますリスト8-(2)⁠。jQueryからajax通信を行うメソッドは処理方式に応じて複数用意されていますが、今回は最も基本的なメソッドである、$.ajaxを使用します。$.ajax は、Javascriptのオブジェクトとして定義されたオプションパラメータを引数にして呼び出します。

$.ajax({オプションパラメータ});

今回定義するオプションパラメータは次の通りです。

dataTypeは、サーバから送信されるデータ形式を指定しますリスト8-(3)⁠。今回はJSONPですので、"jsonp"を指定しました。他には、"xml"、"html"、"json"、"script"、"text"を指定することができます。

dataは、サーバへ送信するデータを指定しますリスト8-(4)⁠。Javascriptのオブジェクト形式で指定します。オブジェクトメンバの名前と値のペアを、自動的に送信クエリに展開してくれます。たとえば、{"name":"aoki", "age":"34"} は、&name=aoki&age=34となります。もちろん、内部で自動的にencodeURIComponentを使ってエンコードしてくれます。

cacheにfalseを指定すると、送信クエリに現在の時刻を使ったランダムな文字列を付与し送信しますリスト8-(5)⁠。これにより、リクエスト毎に必ず異なった送信クエリが生成されるため、Webブラウザはキャッシュを使わずリクエスト毎にデータを取得します。しかし、Youtube APIでは、定義されていないパラメータが送信されるとエラーを返すため、cacheはtrueを指定しています。

urlは、クエリの送信先のURLを指定します。ここではvideo feedを呼び出します。

successはデータ取得に成功した場合に、取得データを引数として呼び出される関数ですリスト8-(6)⁠。

まず、表示領域をクリアします。

$("#videos").empty();

empty()は、マッチした要素の全ての子要素を削除するjQueryのメソッドです。マッチした要素そのものは削除しません。

次に、取得したデータの各要素にアクセスします。jQueryには、$.each(obj, fn)という、Javascriptのオブジェクトや配列への汎用的なイテレーションメソッドが用意されています。1番目の引数(ここではobj)には、イテレーション対象のオブジェクトや配列を指定します。2番目の引数(ここではfn)には、各要素に対しての処理を定義する関数を指定します。ここでは、

$.each(data.feed.entry, function(i,item){
    // 各要素への処理...

});

とするることで、取得したデータのdata.feed.entryの各要素に対する処理を定義していますリスト8-(7)⁠。

2番目の引数fnには、2つの引数が与えられます。1番目の引数(ここではi)には、要素のキーが与えられます。2番目の引数(ここではitem)には、要素の値が与えられます。

よって、この関数内で各entryのmedia$group要素にアクセスするためには次のように記述します。

var group = item.media$group

ここではループの中で処理しやすいように、変数groupに格納しています。

次にプレーヤのページへリンクを張ったサムネイル画像を生成します。 まず、リンク要素を生成しますリスト8-(8)⁠。

DOMプログラミングでは、document.createElementを使用しましたが、jQueryでは、$("<a/>")とするだけで生成することができます。

そのリンク要素のjQueryオブジェクトに対して、attrメソッドを使ってhref属性にプレーヤーへのリンクを設定しますリスト8-(9)⁠。

.attr("href", group.media$player[0].url)

そのリンク要素のjQueryオブジェクトに対して、appendメソッドを使って子要素にサムネイル画像のimg要素を追加しますリスト8-(10)⁠。

.append("<img src='" + group.media$thumbnail[0].url + "'/>")

appendメソッドの処理イメージは次のようになります。

a要素

img要素

そして、そのリンク要素を、appendTo()メソッドを使って、表示領域へ子要素として追加しますリスト8-(11)⁠。

.appendTo("#videos")

ここで、なにかメソッドをたくさん連続して呼び出しているようなこんな記述でいいのだろうか、と思った方もいると思います。

jQueryオブジェクトのメソッドの殆どは、jQueryオブジェクトを返します。 たとえば、リスト8-(8)~8-(11)の戻り値は、すべて a要素のjQueryオブジェクトです。よって、リスト8-(8)~8-(11)のように同じ要素に対してメソッドを何回も呼び出すような場合でも、メソッドを次々と数珠繋ぎのように呼び出すことができます。これを Chainabilityと呼びます。この仕組みのおかげで、ソースコードを簡潔に記述することができます。どうですか?とても直感的で簡潔なコードだと思いませんか?

以上で、実装は終了です。今回のサンプルはこちらです。実際に動かして確認してみてください。

まとめ

今回は、JSON、JSONPを説明しました。JSONとは何か、JSONPはどのような仕組みで実現されているかイメージはつかめたでしょうか。

また、jQueryを使ったAjax実装のサンプルを解説しました。jQueryを使わない第1回の実装と見比べてみると、次のことが分かると思います。

script要素を生成しなくてもよい

script要素の生成は、WebクライアントでJSONPを実現するための実装方法であり、実現したい機能自体とは関係がありません。jQueryがこれを隠蔽してくれるおかげで、ビジネスロジックの実装に集中できます。

コールバック関数を指定しなくてもよい

コールバック関数を指定しなくても、jQueryがcallbackというパラメータとして内部で自動的に生成してくれます。そして、レスポンスデータを引数として処理結果に応じたメソッドに渡してくれます。

Youtube APIのコールバック関数のパラメータはcallbackなので、何も指定しなくてもよいというわけです。

もし、コールバック関数のパラメータがcallbackではない場合は、$.ajaxにjsonpオプションを引数として指定することで上書きすることができます。たとえば、コールバック関数のパラメータがjsonpCallBackの場合は、{"jsonp":"jsonpCallBack"}とします。

HTMLとイベントハンドラが分離されている

フォームのsubmitイベントハンドラを、第1回は次のようにHTMLに埋め込んで記述していました。

<form onsubmit="search(); return false;">

しかし、jQueryを使った今回は、次のようにHTMLにイベントハンドラを記述しませんでした。

<form id="frmSearch">

submitイベントハンドラは、$(function(){ ... }) 内で、jQueryのsubmitメソッドを使って定義しました。このように、HTMLとイベントハンドラが分離されていると、HTMLがシンプルになり、イベント処理が局所化できるため、ソースがとても見やすくなります。

送信クエリを組み立ててくれる

送信クエリの文字列を組み立てる時には、細かい注意が必要です。 パラメータが多くなると、パラメータ文字列とリテラルや変数のペアを作るときにミスがおきやすくなります。また、encodeURIComponentエンコードも忘れがちです。もちろんこれらの処理を扱うライブラリなどを作ればよいのですが、jQueryを使うと、dataオプションにオブジェクトの形式で渡すだけで内部で自動的に生成してくれます。

DOMプログラミングが簡潔に記述できる

jQueryには強力なセレクタがあるため、目的の要素に少ない記述で簡単にアクセスすることができます。また、 Chainabilityの仕組みが記述をより簡潔にします。

今回のサンプルくらいの簡単な実装ですと、jQueryの威力が少し分かりにくいかと思いますが、AjaxでDOMをグリグリいじるようなアプリケーションではとても威力を発揮します。

次回は、Youtube APIを詳しく掘り下げ、jQueryをより使った実装を紹介します。

おすすめ記事

記事・ニュース一覧