技術記事については、Qiitaにも稀に投稿しています。

AjaxにまつわるjQueryとかJSONPとかのあれこれをまとめてみた

glass-globe-graph

Ajax(Asynchronous JavaScript + XML)の仕組みを理解するため、様々なパターンのサンプルソースを動かしてみました。特に、jQueryを用いないパターンは「Ajaxとはなんぞや」という部分の理解に寄与するかと思って。
[toc]

Ajaxとは

まずはAjaxについて、IT用語辞典 e-Wordsから引用。

Ajaxとは、Webブラウザに実装されているJavaScriptのHTTP通信機能を使って、Webページのリロードを伴わずにサーバとXML形式のデータのやり取りを行って処理を進めていく対話型Webアプリケーションの実装形態。

まず、自分の薄っぺらい知識がここで是正されました。Ajaxは新技術の類ではなく、既存のJavaScriptやHTMLの機能を使ったものであるということ。そして、jQueryという言葉は一切出てきません。

扱うサンプル

今回はAjaxの仕組みを理解するのが目的なので、以下のポイントを押さえたサンプルを作って動かします。

  • jQueryを用いないAjax実装
  • JSONオブジェクトを扱うAjax実装
  • クロスドメイン問題に対するscriptタグ実装
  • クロスドメイン問題に対するJSONP実装

jQueryを使わないで実装してみる(3パターン)

同期処理のAjax

まずはAjaxの基本として、jQueryを使わず、同じディレクトリ内のテキストファイルを読み込みます。
(※JSON形式のファイルである必要はありません)

<script type="text/javascript">
    var xmlHttp = new XMLHttpRequest();

    // 同期処理
    function loadText(){
        // 3つ目の引数が同期 or 非同期
        xmlHttp.open("GET", "sample.json", false);
        xmlHttp.send(null);
        alert(xmlHttp.responseText);
    }
</script>
<input type="button" value="同期ファイル読み込み" onClick="loadText()">

読み込むsample.json

{
    "sampleCode" : "100",
    "sampleMessage" : "サンプルメッセージ"
}

実行結果(ボタン押下時)

sample1Result

非同期処理のAjax

上記の同期処理を非同期にしてみます。非同期なので、onreadystatechangeイベントを使い、ファイルの取得が完了した後にalert(xmlHttp.responseText);が動くようにしています。

<script type="text/javascript">
    // 非同期処理
    function loadTextAsync(){
        // readyState: XMLHttpRequestのステータスを保持
        // 0: リクエスト未初期化
        // 1: サーバ接続確立
        // 2: リクエスト受信
        // 3: リクエスト処理中
        // 4: リクエストは終了、レスポンスの準備完了

        // onreadystatechangeイベントに、readyStateプロパティが変わるごとに
        // 自動的に呼ばれる関数(あるいは関数名)を格納
        // readyStateが 4 で、ステータスが 200 である場合、レスポンスの処理は完了
        xmlHttp.onreadystatechange = function(){
            // state-> 200: "OK", 404: Page not found
            alert("readyStateの値: " + xmlHttp.readyState);
            if ((xmlHttp.readyState == 4) && (xmlHttp.status == 200)){

                // レスポンスがjsonならば、そのプロパティのようにアクセス可能
                alert(xmlHttp.response.sampleMessage);
            }
        }
        // HTTPリクエストパラメータを初期化
        xmlHttp.open("GET", "sample.json", true);

        // レスポンスタイプをjsonとする
        xmlHttp.responseType = 'json';

        // HTTPリクエストを送信
        xmlHttp.send(null);
    }
</script>
<input type="button" value="非同期ファイル読み込み" onClick="loadTextAsync()">

実行結果(ボタン押下時)

特に他意はありませんが、今回はテキストファイルとしてではなく、JSONオブジェクトとしてレスポンスを受け取り、プロパティ的にアクセスしています。

sample2Result

また、ボタン押下時、readyStateの値を出力すると、1 -> 2-> 3-> 4と変化します。以下は1の時の例。

sample2-1Result

<script>タグを介したAjax(クロスドメイン対応)

上二つのサンプルはいずれも同一ドメイン上にあるファイルを取得しているので問題はありませんが、もし呼び出し側と呼び出される側が異なるサーバにある場合、同一起源ポリシーに抵触してしまいます。

例えば、kt-kiyoshi.comからkt-kiyoshi.com上のファイルをAjaxで取得しようとすると以下のようになります。

access-control-allow-origin

そこで使うのが<script>タグです。当然ですが、これを使えば、他のサーバ上にあるファイルを読み込むことが可能となります。

読み込むファイルをJSONP形式で記述し、呼び出し側にコールバック関数としてファイルの中身をパースする処理を書きます。

<script type="text/javascript">

    // scriptで外部のファイルを読み込む
    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = 'https://kt-kiyoshi.com/tech/test.json';

    // ドキュメントの最初にあるscriptの前に挿入
    var firstScript = document.getElementsByTagName('script')[0];
    firstScript.parentNode.insertBefore(script, firstScript);

    var getJSON = function(json){
        alert(json.sampleCode);
    }
</script>

読み込むtest.json

getJSON(
    {"sampleCode" : "100","sampleMessage" : "サンプルメッセージ"}
)

なお、呼び出す際のパラメータとして、?callback=getJSONなどと渡し、呼び出し側で任意のコールバック関数名を用いることが多いです。(但し、以下のように呼び出される側が対応している必要があります)

(function(data){
    var list = document.getElementsByTagName('script');
    var temp = list[0].src.match(/[\?\&]callback=([A-Za-z0-9\_\.\[\]]*)/);
    var func = temp ? temp[1] : 'callback';
    eval( func+"(data)" );
})({"sampleCode" : "100","sampleMessage" : "サンプルメッセージ"})

ここまでで、Ajaxを用いたファイルの取得を3パターン実装しました。但し、最近はjQueryを用いた実装が大半だと思うので、以降はjQuery使用パターンで書いてみます。

jQueryを使って実装してみる(3パターン)

jQueryを使ったAjaxでJSONを読み込む

jQueryではガラッと変わって、$.ajaxを使い、呼び出し先のURLや返り値のデータタイプ、パラメータ、レスポンス取得後の処理などを記述していきます。

<script type="text/javascript">
    $( function() {
        $( '#ajax-button-json' ) .click(
            function() {
                $.ajax( {
                    url: 'sample_list.json',
                    dataType : 'json',
                    success: function( data ) {
                        var message = jsonParser(data);
                        $( '#sample-result-json' ).html( message );
                    },
                    error: function( data ) {
                        $( '#sample-result-json' ).html( 'Ajaxエラー' );
                    }
                } );
            }
        );
    } );

    function jsonParser(data) {
        var message = "";
        var count = 0;
        $(data.sample).each( function() {
            message = message + data.sample[count].sampleCode + ' : ' + data.sample[count].sampleMessage + '<br/>';
            count++;
        });
        return message;
    }
</script>
<input type="button" id="ajax-button-json" value="json" />
<div id="sample-result-json"></div>

読み込むsample_list.json

{
    "sample" : [
                {
                    "sampleCode" : "100",
                    "sampleMessage" : "サンプルメッセージA"
                },
                {
                    "sampleCode" : "200",
                    "sampleMessage" : "サンプルメッセージB"
                }
              ]
}

実行結果(ボタン押下時)

ここではalert()での出力ではなく、htmlにアペンドする形でブラウザに出力しています。

sample4Result

クロスドメインのJSONを読み込む(エラー)

次はkt-kiyoshi.com -> kt-kiyoshi.com間のクロスドメイン通信なのでエラーになります。なお、上記ではsuccess, errorの処理をしていますが、以下では.done, .fail, .alwaysを用いています。

<script type="text/javascript">
    $(function(){
        $('#ajax-button-api-json').click(
        function(){
            $.ajax({
                type:'GET',
                // 呼び出し先のドメインが異なります(クロスドメイン)
                url: 'https://kt-kiyoshi.com/tech/sample_list.json',
                dataType: 'json',
                timeout:1000,
            })
            .done(function(responseData){
                console.log("Done!");
                // responseDataにはJSONオブジェクト
                var message = jsonParserListAPIJSON(responseData);
                $('#sample-result-api-json').html(message);
            })
            .fail(function(responseData){
                console.log("Fail!");
            })
            .always(function(responseData){
                console.log("Always!");
            });
        });
    } );

    function jsonParserListAPIJSON(data) {
        return data.sampleMessage;
    }
</script>
<input type="button" id="ajax-button-api-json" value="click" />
<div id="sample-result-api-json"></div>

読み込むsample_list.jsonは上のサンプルと同様です。が、これはエラーになるので取得できません。

クロスドメインのJSONPを読み込む

クロスドメイン通信に対応するため、呼び出される側をJSONPとします。jQueryを用いたAjaxの主流はこれです。

<script type="text/javascript">
    $(function(){
        $('#ajax-button-api-jsonp').click(
        function(){
            $.ajax({
                type:'GET',
                url: 'https://kt-kiyoshi.com/tech/test.json',
                dataType: 'jsonp',
                // jsonp: "callback", // jsonpのコールバック関数キー(default: callback)
                jsonpCallback: 'getJSON', // jsonpのコールバック関数名
                timeout:1000,
            })
            .done(function(responseData){
                console.log("Done!");
            })
            .fail(function(responseData){
                console.log("Fail!");
            })
            .always(function(responseData){
                console.log("Always!");
            });
        });
    } );

    function getJSON(data) {
        $('#sample-result-api-jsonp').html(data.sampleMessage);
    }
</script>
<input type="button" id="ajax-button-api-jsonp" value="click" />
<div id="sample-result-api-jsonp"></div>

再掲ですが、呼び出し先は以下です。

読み込むtest.json

getJSON(
    {"sampleCode" : "100","sampleMessage" : "サンプルメッセージ"}
)

jQueryを用いずクロスドメイン問題を対応した<script>タグとは似て非なる感じですが、こちらのほうがリクエストがすっきりしていて見やすいかなと思います。


まとめ

以上、Ajax(Asynchronous JavaScript + XML)の様々な実装パターンでした。

序盤の実装方法は(jQueryを使えない環境でもない限り)今後ほとんど使わないとは思いますが、Ajaxという仕組みの根本であることは間違いないので知っておくに越したことはないと思います。