Baccho Log

No Image

Sponsored Link

Ajaxの使い方

  • 投稿日:
  • 更新日:
Tags:
JavaScript jQuery
Categories:
プログラミング

概要

非同期なHTTP通信を実装出来ます。
指定したURLを読み込み、読み込んだデータを取得します。

いきなり所感

私的にはサーバ側のデータを受取るというのがキモだと思ってます。
最初、フォームからの入力情報をうにゃうにゃするという目的で調べ始めたため、
どうにも頭が固くなってましたが、別にformのデータをどうにかするんじゃなくてもいいんですよね。

formからの入力情報を、php等で処理することも出来るよってだけで、
例えば、こちらから情報は渡さず、別のhtmlファイルやtxtファイルを受取るだけってことも出来ますし、
受取ったファイルを表示することも出来ます。

基本的には、URLで指定した情報を受取る(その際、付属でこっちからデータを渡して、サーバ側で処理も出来るよ)
と覚えると、覚えやすいかもしれません。

jQuery

$.ajaxメソッド

構文

  1. ajax( object )**
  2. ajax( url [,object] )

戻り値は promise Object(deferred.promise()の戻り値) を返します。

objectのプロパティの種類が多すぎるため、私的に主に使用するもののみを記載します。
もっと詳細を確認する際は、公式か下記の参考サイトをご確認下さい。

  • url
    リクエスト送信先のURLを記載します。
    規定値は自身のURL。
  • type
    送信形式を指定します。getpost
    規定値はget
  • data
    サーバに送信するデータを設定します。
    配列の場合は、同じ変数名でシリアライズする必要があります。
  • timeout
    タイムアウトの時間をミリ秒で指定します。
  • dataType
    サーバからの受信形式を指定します。
    省略すると、jQuery側で自動的に設定を行ないます。
    明示的に指定する際は、"text", "html", "xml", "script", "json", "jsonp" のいずれかで設定します。
  • async
    非同期の有無を設定します。
    規定値はtrue(非同期通信)

jQuery 公式
とほほのjQuery入門

この中でも、timeoutは設定する事をオススメします。
ユーザの通信環境によっては、かなりの時間に処理が掛かってしまうことも考えられます。
完了するまでひたすらに通信を行なうのは、ユーザ離脱の原因にもなりますので
一定時間が過ぎたらajax通信を停止する設定をする事をオススメします。

Qiita | AJAX通信をするときはタイムアウト処理を必ず入れてほしい(切実)

処理後のステータス

通信が成功したか、失敗したか等を指定します。
実際にコードを見たほうが早いと思いますので、記載します。

/* sample.txtを受取って表示します */
/* 例1 */
$.ajax({
    url: 'sample.txt',
    dataType: 'text',
    timeout: 50000
})
.done(function(data, status, xhr) {
    // 通信成功時の処理
    console.log(data);   // <= txtデータの内容
    console.log(status); // <= success
})
.fail(function(xhr, status, error) {
    // 通信失敗時の処理
    console.log(status); // <= parseerror
    console.log(error);  // <= エラー内容
})
.always(function(arg1, status, arg2) {
    // どちらにしても通信完了した際の処理
});

/* 例2 */
$.ajax({
    url: 'sample.txt',
    dataType: 'text',
    timeout: 50000
})
.then(function(data, status, xhr) {
    // 通信成功時の処理
    console.log(data);   // <= txtデータの内容
    console.log(status); // <= success
}, function(xhr, status, error) {
    // 通信失敗時の処理
    console.log(status); // <= parseerror
    console.log(error);  // <= エラー内容
});

例1のalwaysの引数については、doneとfailで変わります。
引数の内容はそれぞれの引数と同じです。

  • done
    1. data
      受信データです
    2. status
      通信結果のステータスです
      "success", "notmodified", "error", "timeout", "abort", "parseerror"
      のいずれかの結果が文字列で返ります。
    3. xhr
      XMLHttpRequestオブジェクトを取得します。
      IEの場合は、ActiveXObjextです。
  • fail
    1. xhr
      doneの第三引数と同様
    2. status
      doneの第二引数と同様
    3. error
      エラー内容が入ってます。

例2のthenも引数は同様です。
thenを続けて書く事も出来ます。

$.ajaxSetupメソッド

$.ajaxメソッドのObjectを事前に設定できます。
敢えて言わなくてもいいかもですが、$.ajaxメソッドより先に宣言する必要があります。
また、$.ajaxメソッド側でObjectの追加や上書きも出来ます。

$.ajaxSetup({
    url: 'sample.txt',
    dataType: 'text',
    timeout: 50000
});

$.ajax()
.then(function(data, status, xhr) {
    console.log(status);
}, function(xhr, status, error) {
    console.log(status);
});

/* 下記のように$.ajaxメソッドで追加や上書きも出来る */
$.ajax({
    // 設定追加
    async: false,
    // 設定上書き
    url: 'ajax_test.php',
    dataType: 'json'
})
.then(function(data, status, xhr) {
    console.log(status);
}, function(xhr, status, error) {
    console.log(status);
});

とは言え、やはりformでしょう

いきなり所感で、formじゃなくてもいいんだよ!とか言っておりますが、
それでもformで情報を送信して、送信した情報をサーバ側で処理したものを受取るというケースは多いと思います。(私も元々それで調べてますし)

その際の使用例や、便利なメソッドなども下記に記載します。

シリアライズ

ajaxでdataを一つずつ指定してもいいですが、serializeメソッドを使用すると一発で変換できます。
ただし、<input type="checkbox"> はチェックが付いていないとシリアライズされません。
また、<input type="file"> もシリアライズされません。

serializeメソッド

formの入力値をクエリ文字列に変換します。
クエリ文字は、name=&mail=&message という、get のような文字列

serializeArrayメソッド

formの入力値を配列に変換します。
配列の中にオブジェクトが入っており、nameプロパティとvalueプロパティが生成されます。

下記のような配列内容に変換されます。

serialize = [
    {name: 'name', value: 'My Name'},
    {name: 'mail', value: 'example@mail.com'},
    {name: 'sex', value: 'male'},
    {name: 'message', value: 'I am a human'}
];

serialize[0].name;  // <= name
serialize[0].value; // <= My Name

submit

formでbuttonのtypeを指定しない場合はsubmitが走ります。

<form>
    <input type="text" placeholder="text"><br>
    <button>送信</button>
</form>

submitさせたくないときは、
html側で対応する際は、typeをbuttonに設定する 。
js側で対応する際は、
return false するか、 preventDefaultメソッドを使用します。

preventDefaultメソッド

<form>
    <input type="text" placeholder="text"><br>
    <button type="button">送信</button>
</form>

もしくは

// return false
$('button').click(function() {
    // ここに処理...
    return false;
});

// preventDefaultメソッド
$('button').click(function(e) {
    e.preventDefault();
    // ここに処理...
});

使用例

ページをリロードさせずに、ajax_test.phpに情報を送信して、処理した結果を受取る
<form id="pageForm">
    <input type="text" name="name" value="" placeholder="Name"><br>
    <input type="email" name="mail" value="" placeholder="Mail"><br>
    <label><input type="radio" name="sex" value="male">男性</label>
    <label><input type="radio" name="sex" value="female">女性</label><br>
    <label><input type="checkbox" name="check">何かを希望する</label><br>
    <textarea name="message" placeholder="Messages"></textarea><br>
    <button>送信!</button>
</form>
<div id="result">ここに結果を表示</div>
$(function() {
    $('button').click(function(event) {
        event.preventDefault();
        var $form = $('#pageForm');
        var serialize = $form.serialize();
        $.ajax({
            url: 'ajax_test.php',
            dataType: 'json',
            data: serialize,
            type: 'post'
        })
        .done(function(j_data) {
            // 成功した場合の処理...
            var str = "Name: " + j_data.name + "<br>";
            str += "E-Mail: " + j_data.mail + "<br>";
            str += "性別: " + j_data.sex + "<br>";
            str += "Check: " + j_data.check + "<br>";
            str += "Message: " + j_data.message + "<br>";
            $('#result').html(str);
        })
        .fail(function() {
            // 失敗した場合の処理...
            $('#result').text("失敗しました");
        });
    });
});
<?php
    $val = [];

    foreach ($_POST as $key => $value) {
        if($key !== 'check') $val[$key] = $value;
    }

    $val['check'] = isset($_POST['check']) ? true : false;
    echo json_encode($val);

サンプル

フォームではなく、ただtxtファイルを取得する。
<input type="button" value="送信!">
<div id="result">ここに結果を表示</div>
$(function() {
    $('input[type=button]').click(function() {
        $.ajaxSetup({
            url: 'ajax_test.txt',
            dataType: 'text',
            type: 'post'
        });
        $.ajax()
        .then(function(j_data) {
            $('#result').text(j_data);
        }, function() {
            $('#result').text("失敗しました");
        });
    });
});

二つ以上のデータを非同期に処理したい

さて、これまでの例では一つのデータを処理する方法を記載しました。
では、二つ以上のデータを処理したい場合はどうすればいいでしょうか。

まずは、単純に増やしてみます。

$(function() {
    $('button').click(function(e) {
        e.preventDefault();
        var $form = $('#pageForm');
        var serialize = $form.serialize();

        // 1つめのデータ
        $.ajax({
            url: 'ajax_test.php',
            dataType: 'json',
            data: serialize,
            type: 'post'
        })
        .then(function(j_data) {
            var str = "Name: " + j_data.name + "<br>";
            str += "E-Mail: " + j_data.mail + "<br>";
            str += "性別: " + j_data.sex + "<br>";
            str += "Check: " + j_data.check + "<br>";
            str += "Message: " + j_data.message + "<br>";

            // 二つめのデータ
            $.ajax({
                url: 'test.txt',
                dataType: 'text',
                type: 'post'
            })
            .then(function(t_data) {
                // 成功した場合の表示
                $('#result').html(str + t_data);
            }, function(status) {
                // 失敗した場合の表示
                $('#result').text(status);
            });
        });
    });
});

参考

$.ajaxメソッドをネストする事で、まぁ一応処理は出来てます。
ただ可読性良くないですよね。直列処理をするのであればthenで繋ぐ事が出来ます。

$(function() {

    // --- 以下、ajaxまで省略 --- //

    // 1つめのデータ
    $.ajax({
        url: 'ajax_test.php',
        dataType: 'json',
        data: serialize,
        type: 'post'
    })
    .then(function(j_data) {
        var str = "Name: " + j_data.name + "<br>";
        str += "E-Mail: " + j_data.mail + "<br>";
        str += "性別: " + j_data.sex + "<br>";
        str += "Check: " + j_data.check + "<br>";
        str += "Message: " + j_data.message + "<br>";
        $('#result').html(str);

        // 二つめのデータを戻り値で返す
        return $.ajax({
            url: 'test.txt',
            dataType: 'text',
            type: 'post'
        });
    })
    .then(function(t_data) {
        // 二つ目のデータ
        $('#result').html(t_data);
    });
});

こんな感じで、戻り値として$.ajaxを返すと、ネストさせることもなく書く事ができます。
then区切りでネストもないし、大分見やすくなったと思います。
ただし、ここでエラー時の処理も書くとどうでしょうか。

$(function() {

    // --- 以下、ajaxまで省略 --- //

    // 1つめのデータ
    $.ajax({
        url: 'ajax_test.php',
        dataType: 'json',
        data: serialize,
        type: 'post'
    })
    .then(function(j_data) {
        var str = "Name: " + j_data.name + "<br>";
        str += "E-Mail: " + j_data.mail + "<br>";
        str += "性別: " + j_data.sex + "<br>";
        str += "Check: " + j_data.check + "<br>";
        str += "Message: " + j_data.message + "<br>";
        $('#result').html(str);

        // 二つめのデータを戻り値で返す
        return $.ajax({
            url: 'test.txt',
            dataType: 'text',
            type: 'post'
        });
    }, function() {
        console.log('error');
        // ここでも二つめのデータを戻り値で返す
        return $.ajax({
            url: 'test.txt',
            dataType: 'text',
            type: 'post'
        });
    })
    .then(function(t_data) {
        // 二つ目のデータ
        $('#result').html(t_data);
    }, function() {
        console.log('error');
    });
});

戻り値を成功時と失敗時の二つに設定しないといけないとか、結構面倒くさいです。
コードも無駄に長くなってしまってる気もします。
そこで$.Deferredメソッドを使用します。

$.Deferredメソッド

肝となる箇所は、下記の三点だと思います。

  1. deferred.promise()
    タスク(タスクをこなしますという約束)
  2. deferred.resolve()
    タスクが正常に終わったことを通知(引数に指定した内容が、done()に引き渡される)
  3. deferred.reject()
    タスクが異常終了した事を通知(引数に指定した内容が、fail()に引き渡される)

色々な使い方が出来ますので、あくまで一例ですが
promiseを返す関数を作って可読性よく処理できる例を作ってみたいと思います。

直列処理

var async = function(option) {
    option = option || {
        url: 'test.php',
        dataType: 'text',
        data: {param: 'hoge'}
    };
    var $ajax = $.ajax(option);
    var defe = new $.Deferred();

    $ajax
    .then(function(data, status, xhr) {
        console.log("OK: 1");
        defe.resolve(); // ajax通信に成功した場合、deferred の promise を resolve に設定する
    }, function(xhr, status, err) {
        console.log("NG: 1");
        defe.reject(); // ajax通信に失敗した場合、deferred の promise を reject に設定する
    });
    return defe.promise();
};

// asyncとほぼ同様
var async2 = function(option) {
    option = option || {
        url: 'test2.php',
        dataType: 'text',
        data: {param: 'hoge'}
    };
    var $ajax = $.ajax(option);
    var defe = new $.Deferred();

    setTimeout(function() {
        $ajax
        .then(function(data, status, xhr) {
            console.log("OK: 2");
            defe.resolve();
        }, function(xhr, status, err) {
            console.log("NG: 2");
            defe.reject();
        });
    }, 1000);
    return defe.promise();
};

var failed = function() {
    console.log('error');
};

async()
.then(async2, failed)
.then(async, failed)
.then(async2, failed)
.then(async, failed);
// 全て成功した場合、
// OK: 1 => 1秒後に OK: 2 => OK: 1 => 1秒後に OK: 2 => OK: 1
// という上から順番に処理される。

// async2 が失敗した場合
// OK: 1 => 1秒後に NG: 2 => OK: 1 => 1秒後に NG: 2 => OK: 1
// という処理がされる。

次に、失敗例です。
thenのコールバック関数に引数を指定した場合 もしくは 括弧を付けて宣言した場合 は並列的に処理が進みます。
以下、全て並列的に進みます。

var async = function(option) {
    option = option || {
        url: 'test.php',
        dataType: 'text',
        data: {param: 'hoge'}
    };
    var $ajax = $.ajax(option);
    var defe = new $.Deferred();

    $ajax
    .then(function(data, status, xhr) {
        console.log("OK: 1");
        defe.resolve();
    }, function(xhr, status, err) {
        console.log("NG: 1");
        defe.reject();
    });
    return defe.promise();
};

// asyncとほぼ同様
var async2 = function(option) {
    option = option || {
        url: 'test2.php',
        dataType: 'text',
        data: {param: 'hoge'}
    };
    var $ajax = $.ajax(option);
    var defe = new $.Deferred();

    setTimeout(function() {
        $ajax
        .then(function(data, status, xhr) {
            console.log("OK: 2");
            defe.resolve();
        }, function(xhr, status, err) {
            console.log("NG: 2");
            defe.reject();
        });
    }, 1000);
    return defe.promise();
};

var failed = function() {
    console.log('error');
};

/* ケース1 */
async()
.then(async2({url: 'test2.php'}), failed)
.then(async, failed)
.then(async2, failed)
.then(async, failed);

/* ケース2 */
async()
.then(async2(), failed)
.then(async, failed)
.then(async2, failed)
.then(async, failed);

/* ケース3 */
async()
.then(async2, failed)
.then(async(), failed)
.then(async2, failed)
.then(async, failed);

/* ケース4 */
async()
.then(async2, failed)
.then(async({url: 'test.php'}), failed)
.then(async2, failed)
.then(async, failed);

Qiita | はじめてajaxを使うときに知りたかったこと

引数を指定する際に、直列処理を行ないたい場合は、無名関数で返すようにします。

// ~~~~省略~~~~~

/* ケース1 */
async()
.then(function() { return async2({url: 'test2.php'}); }, failed)
.then(async, failed)
.then(async2, failed)
.then(async, failed);

/* ケース2 */
async()
.then(async2, failed)
.then(async, function() { return failed(); })
.then(function() { return async2(); }, failed)
.then(async, failed);

こんな感じで引数を指定する場合は、無名関数でreturnすれば直列処理がされます。

jQuery deferredの使い方 – deferredの基本 | CodeGrid

※補足※
今回、個別にDeferredを使用して、promiseを返してますが
ajaxを使用する際は、ajaxをそのまま返しても問題ありません。(むしろスッキリ記述出来るまである)
ajaxもDeferredを持っており、戻り値としてpromiseを返します。
なので、今回のケースではDeferredを使用する必要はなく、本来はajaxを返した方がスマートです。

並列処理

並列処理を行なう場合、$.whenメソッドを使用すると簡単に出来ます。

$.whenメソッドを使おう

構文

$.when( 処理1, 処理2, 処理3,… )
引数に入れた項目を非同期で処理します。

使用例

まずは、簡単な使用例を確認しましょう。

/* 例1 */
/* ajax を変数でまとめて使用する場合 */
var data1 = $.ajax({
    url: 'test.php',
    dataType: 'text',
    data: {param: 'hoge'}
});
var data2 = $.ajax({
    url: 'test.php',
    dataType: 'text',
    data: {param: 'fuga'}
});
var data3 = $.ajax({
    url: 'test.php',
    dataType: 'text',
    data: {param: 'foo'}
});

$.when(data1, data2, data3)
.then(function(data1, data2, data3) {
    console.log(data1);
    console.log(data2);
    console.log(data3);
}, function(xhr, status, err) {
    console.log(status + err);
});


/* 例2 */
/* post や get 等を使用する場合 */
$.when(
    $.post('test.php', {param: 'hoge'}),
    $.post('test.php', {param: 'fuga'}),
    $.post('test.php', {param: 'foo'})
)
.then(function(data1, data2, data3) {
    console.log(data1);
    console.log(data2);
    console.log(data3);
}, function(xhr, status, err) {
    console.log(status + err);
});

個人的にはそこまで細かい指定をしないのであれば、ajaxメソッドを使用するよりも、
$.postメソッド$.getメソッド等の違うメソッドを使用したほうがいいのではないかなと思います。
もし、細かい指定をするのであれば、$.ajaxメソッドを変数に纏めるのがいいと思います。

whenの引数で指定した変数を並列処理を行い、
thenの引数では、whenで指定した引数順にカンマ区切りでHTTP通信したデータが格納されます。
今まで、thenの引数はthen(data, status, jqXHR)でしたが、それが各オブジェクトごとに配列で渡されます。
例えば、今回の例では、then(data1, data2, data3)とありますが、data1, data2, data3の中に
それぞれdata, status, jqXHRが格納されてます。
つまり、

data1[0] => hoge
data1[1] => success
data1[2] => XHR

という風な感じになります。

とても便利ではありますが、このままだと問題があります。
一つでも処理が失敗した場合、resolve されず rejectが返ります。

var data1 = $.ajax({
    url: 'test.php',
    dataType: 'text',
    data: {param: 'hoge'}
});
var data2 = $.ajax({
    url: 'test.php',
    dataType: 'text',
    data: {param: 'fuga'}
});
var data3 = $.ajax({
    url: 'error.php',
    dataType: 'text',
    data: {param: 'foo'}
});

$.when(data1, data2, data3)
.then(function(data1, data2, data3) {
    console.log(data1);
    console.log(data2);
    console.log(data3);
}, function(xhr, status, err) {
    console.log("どこかでエラーが起きている");
});

これはdata3でファイルが存在しないためエラーが起きていますが、promiseの結果がrejectになるので
他の成功した処理に関係なく、失敗となります。
また、通常どこでエラーが起きているのか特定するのも難しいです。

エラーが起きても、通信に成功したものは結果を出して、失敗したものだけをエラーとしたい場合も結構あります。
そして、どれでエラーが発生したのかも特定したいです。
そこでまた$.Deferredメソッドを使用します。

/* 例1_1 */
var myAjax = function(option) {
    var defer = $.Deferred();
    var $ajax = $.ajax(option);

    $ajax
    .then(function(data, status, jqXHR) {
        // this.type は ajax の context で指定している値
        console.log(this.type + ', OK: ' + data);
        defer.resolve();
    }, function(jqXHR, status, err) {
        console.log(this.type + ', NG: ' + status);
        defer.resolve(); // 失敗してもresolve
    });
    return $.extend(false, $ajax, defer.promise());
};

var defer_list = [];
for (var i = 0; i < 3; i++) {
    defer_list.push(myAjax({url: 'test.php', data: {param: 'param' + i}, context: {type: i}, dataType: 'text', type: 'post'}));
}

$.when.apply(null, defer_list)
.then(function() {
    console.log('全て成功');
}, function() {
    console.log('どこかで失敗');
});


/* 例1_2 */
var myAjax = function(option) {
    var defer = $.Deferred();
    var $ajax = $.ajax(option);

    $ajax
    .then(function(data, status, jqXHR) {
        // 左から順番に done のコールバック関数の引数に渡される
        defer.resolve(this.type, status, data);
    }, function(jqXHR, status, err) {
        defer.resolve(this.type, status); // 失敗してもresolve
    });
    return $.extend(false, $ajax, defer.promise());
};

var defer_list = [];
for (var i = 0; i < 3; i++) {
    defer_list.push(myAjax({url: 'test.php', data: {param: 'param' + i}, context: {type: i}, dataType: 'text', type: 'post'})
        .done(function(context, status, data) { // resolve の引数で指定した値が順番で入る
            if (status === 'success') {
                console.log(context + ', :OK ' + data);
            } else {
                console.log(context + ', :NG ' + status);
            }
        })
    );
}

$.when.apply(null, defer_list)
.then(function() {
    console.log('全て成功');
}, function() {
    console.log('どこかで失敗');
});
/* 例2 */
var myAjax = function(option) {
    var defer = $.Deferred();
    var $ajax = $.ajax(option);

    $ajax
    .then(function(data, status, jqXHR) {
        defer.resolveWith(this, arguments);
    }, function(jqXHR, status, err) {
        defer.resolveWith(this, arguments);
    });
    return $.extend(false, $ajax, defer.promise());
};

var defer_list = [];
for (var i = 0; i < 3; i++) {
    defer_list.push(myAjax({url: 'test.php', data: {param: 'param' + i}, context: {type: i}, dataType: 'text', type: 'post'})
        .done(function(data, status, jqXHR) {
            if (status === 'success') {
                console.log(this.type + ', :OK ' + data);
            } else {
                console.log(this.type + ', :NG ' + status);
            }
        })
    );
}
$.when.apply(null, defer_list)
.then(function() {
    console.log('全て成功');
}, function() {
    console.log('どこかで失敗');
});

例1_1では、ajax通信が成功しても失敗しても、deferredのpromiseがresolveが設定されます。
extendメソッド(後述)で、ajaxのpromiseがdeferredのpromiseに上書きされます。
例1_2も、やっていることは同様ですが、myAjaxの内部で結果を出すのではなく、
resolveの引数に値を入れて呼び出し側で結果を出すようにしています。

例2でも、基本的にはやっていることは同じです。ajax通信の成否に関わらずresolveが返ります。
但し、resolveWithが指定されています。
resolveWithについては、promiseの結果をresolveにするapply(後述)みたいな感じで覚えるといいと思います。
軽く説明すると、done のコールバック関数内で、thisとして利用したい値を第一引数で指定する事が出来ます。
第二引数は通常のresolveの引数を配列で指定します。
今回の場合、argumentsを指定してますのでdata, status, jqXHRが順番に指定されます。
余談ですが、rejectWithというものもあります。

Hatena Blog | Query.DeferredとかjQuery.whenの使い方について – 一から勉強させてください( ̄ω ̄;)

applyとは何ぞ?

applyの利用方法 | JavaScript capsule
Function.prototype.apply() – JavaScript | MDN

functionの中で、thisを設定している場合
そのthisの中身を好きな値に置換することが出来ます。

var obj = {
    name: 'Hatsune'
};
var test = function(str1, str2, str3) {
    this.name = this.name + " " + str1 + " " + str2 + " " + str3;
    console.log(this.name);
};
test('Niku', 'Miku', 'Dayo');               // Niku Miku Dayo
test.apply(obj, ['Niku', 'Miku', 'Dayo']);  // Hatsune Niku Miku Dayo

さっきの$.when.apply(null, defer_list)については、
thisの中身はnull、引数はforで回した分の ajax が配列で入っているので、
$.when(defer_list[0], defer_list[1], defer_list[2]);と同義です。

contextとは何ぞ?

[jQuery] 1.ajaxのコールバック関数にcontextで値を渡す | きほんのき

これもapplyのような感覚に似てます。
contextで指定した値を、thisで受取る事が出来ます。

/* objectで指定する例 */
$.ajax({
    url: 'test.php',
    data: {param: 'param' + i},
    context: {type: 1},
    dataType: 'text',
    type: 'post'
})
.done(function(data, status, jqXHR) {
    console.log(this.type); // 1
});


/* そのまま値を指定する例 */
$.ajax({
    url: 'test.php',
    data: {param: 'param' + i},
    context: 1,
    dataType: 'text',
    type: 'post'
})
.done(function(data, status, jqXHR) {
    console.log(this); // 1
});

$.extendメソッド

Objectの結合(マージ)を行ないます。

構文

$.extend( [deep,] target [,object_1] [,object_2] [.....,object_N] )

  • deep
    真偽値。オブジェクトのdeepな箇所を参照します。
  • target
    Object。ターゲットとなるオブジェクトを指定します。
  • object
    連結させるオブジェクトを指定します。
    同名のキーがある場合は、後にあるものが上書きされます。
$('button').click(function(e) {
    var obj_1 = {
        orange: 100,
        apple: 150,
        banana: 200
    };
    var obj_2 = {
        pine: 600,
        dragon: 5000
    };
    var exObj = $.extend(obj_1, obj_2);
    var str = "";
    for(var key in exObj) {
        str += key + "の値段は" + exObj[key] + "です。<br>";
    }
    $('#result').html(str);
    console.log(obj_1); // Object {orange: 100, apple: 500, banana: 200, pine: 600, dragon: 5000}
    console.log(obj_2); // Object {pine: 600, dragon: 5000}
    console.log(exObj); // Object {orange: 100, apple: 500, banana: 200, pine: 600, dragon: 5000}
});

サンプル

これは、obj_1をターゲットにしてobj_2を連結させています。
exObjという変数にオブジェクトを返して、for文で回してます。
obj_1obj_2が連結された中身が、exObjという変数に入っているのが確認できると思います。

さて、ここでもう一つ、コンソールを確認して下さい。
obj_2exObjについては、思惑通りの値になっているかと思いますが、
ターゲットのobj_1の値もexObjと同様になっているのが分かります。

これはobj_1exObjに参照渡ししています。
先ほどの例に加えてみましょう。

exObj.orange = 1500;
obj_1.dragon = 9999;
console.log(obj_1); // Object {orange: 1500, apple: 500, banana: 200, pine: 600, dragon: 9999}
console.log(exObj); // Object {orange: 1500, apple: 500, banana: 200, pine: 600, dragon: 9999}

参照してるオブジェクトを渡してますので、片方を変えれば両方とも変わります。

でも、参照渡しされると困るというか、値渡し(オブジェクト内容を複製するだけ)したいときってありますよね。
下記の参考ページのようにforで回したりでもいいんですけど、
Qiita | 【Javascript】値渡しと参照渡しについてあらためてまとめてみる
$.extendメソッドの第一引数に空のオブジェクトを入れることで解消できます。

$('button').click(function(e) {
    var obj_1 = {
        orange: 100,
        apple: 150,
        banana: 200
    };
    var obj_2 = {
        pine: 600,
        dragon: 5000
    };
    var exObj = $.extend({}, obj_1, obj_2);
    exObj.orange = 1500;
    obj_1.banana = 500;
    console.log(obj_1); // Object {orange: 100, apple: 500, banana: 500}
    console.log(exObj); // Object {orange: 1500, apple: 500, banana: 200, pine: 600, dragon: 5000}
});

サンプル

また、第一引数をdeepにして、deepな参照をfalseに設定しても同様の結果になります。

$('button').click(function(e) {
    var obj_1 = {
        orange: 100,
        apple: 150,
        banana: 200
    };
    var obj_2 = {
        pine: 600,
        dragon: 5000
    };
    var exObj = $.extend(false, obj_1, obj_2);
    exObj.orange = 1500;
    obj_1.banana = 500;
    console.log(obj_1); // Object {orange: 100, apple: 500, banana: 500}
    console.log(exObj); // Object {orange: 1500, apple: 500, banana: 200, pine: 600, dragon: 5000}
});

オブジェクトのプロパティ値を増やすパターンも確認しましょう。

$('button').click(function(e) {
    var obj_1 = {
        orange: {
            price: 100
        }
    };
    var obj_2 = {
        orange: {
            from: "愛媛県"
        }
    };
    var exObj = $.extend(false, obj_1, obj_2);
    console.log(obj_1); // Object {orange: Object { price: 100 }}
    console.log(exObj); // Object {orange: Object { from: 愛媛県 }}
});

親のプロパティ名がorangeで重複しているため、上書きされてしまっています。
これはdeepをtrueにすれば解決します。

$('button').click(function(e) {
    var obj_1 = {
        orange: {
            price: 100
        }
    };
    var obj_2 = {
        orange: {
            from: "愛媛県"
        }
    };
    var exObj = $.extend(true, obj_1, obj_2);
    console.log(obj_1); // Object {orange: Object { price: 100, from: 愛媛県 }}
    console.log(exObj); // Object {orange: Object { price: 100, from: 愛媛県 }}
});

参照渡しなので注意して下さい。

サンプル

余談

ちなみに、大体は$.get$.postメソッドで事足ります。
$.ajaxメソッドの簡易版のような感じですので、軽く説明して終わります。

$.getメソッド

構文

$.get( url [,object] [,function] [,type] )
urlは第一引数。
送るデータは第二引数
第三引数のfunctionは成功した時のみに発火します。
dataTypeは第四引数で指定して下さい。

$(function() {
    $('button').click(function(event) {
        event.preventDefault();
        var $form = $('#pageForm');
        var serialize = $form.serialize();

        $.get('ajax_test.php', serialize, function(j_data) {
            var str = "Name: " + j_data.name + "<br>";
            str += "E-Mail: " + j_data.mail + "<br>";
            str += "性別: " + j_data.sex + "<br>";
            str += "Check: " + j_data.check + "<br>";
            str += "Message: " + j_data.message + "<br>";
            $('#result').html(str);
        }, 'json');
    });
});

jQuery リファレンス:jQuery.get

ちなみに、then等も使えますので、複数ファイルの直列処理も可能です。
いいのかどうかは分かりませんが、第三引数のコールバック関数をnullに設定してます。
これは、「コールバック関数の引数」と「thenのコールバック関数の引数」が同じデータを持ってくるためです。

/* 成功例 */
$.post('ajax_test.php', serialize, null, 'json')
.then(function(j_data) {
    console.log(j_data);
    return $.post('test.txt');
})
.then(function(t_data) {
    console.log(t_data);
});


/* 失敗例 */
// 第三引数でreturnをしてもだめ
$.post('ajax_test.php', serialize, function(j_data) {
    console.log(j_data);
    return $.post('test.txt');
}, 'json')
.then(function(t_data) {
    // これも ajax_test.phpの通信データ
    console.log(t_data);
});

JavaScript

  • ajax
    ⇒ XMLHttpRequest もしくは fetch(但しfetchは対応ブラウザが安定していないため、まだ実用は難しい)
  • serialize
    ⇒ FormData
  • deferred
    ⇒ Promise

XMLHttpRequestオブジェクト

XMLHttpRequest xhr = new XMLHttpRequest();

メソッド

  • open(type, url, [async[, username[, pass]]])
    1. HTTP メソッドを指定します。
      "GET"、"POST"、"HEAD"、"PUT"、"DELETE"、"OPTIONS" など
    2. HTTP通信先のURLを指定します
    3. 非同期を真偽値で指定します。
      規定値はtrue(非同期)
    4. 認証が必要なページには、予めユーザ名を指定することが出来ます。
      (省略した場合、ダイアログが表示されます)
    5. 認証が必要なページには、予めパスワードを指定することが出来ます。
      (省略した場合、ダイアログが表示されます)
      戻り値はありません。
  • overrideMimeType(MIMEType)
    sendメソッド実行前にMIME Type を上書きします。
  • send([sendData])
    送信データを指定します。
    openメソッドでPOSTを指定したときにのみ指定をします。
  • abort()
    HTTP通信を中止します。
    中止されると、onreadystatechange, onloadend, onabortイベントが発行されます。
  • setRequestHeader(headerType, value)
    1. ヘッダの種類を指定します。”Content-Type”一択で考えていいと思います。
    2. 第一引数のフォーマットを指定します。
      1. text/plain テキスト文書
      2. text/html HTML 文書
      3. application/xml XML文書
      4. application/json JSON文書
      5. application/x-www-form-urlencoded 『変数名=値&変数名=値&変数名=値&変数名=値』の形式
        例えば「user=taro」「age=18」「blood=b」という3つのパラメータを送信したい場合『user=taro&age=18&blood=b』
  • getResponseHeader(headerType)
    指定したレスポンスヘッダ(クライアント側に返す際のヘッダ)情報を1つ取得します。
  • getAllResponseHeaders()
    全てのレスポンスヘッダ情報を取得します。

プロパティ

  • readyState
    HTTP通信の状況を取得します。(0 – 4)
  • status
    HTTP通信の状態を取得します。(200, 404, 500等)
  • statusText
    HTTP通信の状態を文字列で取得します。(200 OK, Not Found等)
  • responseText
    レスポンスボディをString型で取得します。
  • responseXML
    レスポンスボディをDocument型で取得します。
  • responseType
    レスポンスボディのデータ型を指定します。responseプロパティと併せて使用します。
  • response
    レスポンスボディを任意のデータ型(responsTypeで指定した型)で取得します。
  • upload
    XMLHttpRequestUpload オブジェクトを取得します。
    XMLHttpRequestUploadは送信中のイベントを発生させることが出来ます。POSTを指定する必要があります。
  • timeout
    タイムアウトまでの時間をミリ秒で指定できます。(省略すると0になります)
  • withCredentials
    クロスサイト Access-Control リクエストにcookie や認証ヘッダといった認証情報を使用させるかを示します。
    真偽値のtrueを入れると、Cookie などの認証情報を送信します。

イベント

  • onreadystatechange
    HTTP通信の状態が変化するたびに実行されます。(readyStateの値が変わるたびと覚えるといいかも)
  • onloadstart
    HTTP通信を開始したときに実行されます。
  • onprogress
    HTTP通信中に繰り返し実行されます。
  • onloadend
    成否に関係なく、HTTP通信が終了したら実行されます。
  • onload
    HTTP通信が成功したときに実行されます。
  • onerror
    HTTP通信が失敗したときに実行されます。
  • onabort
    HTTP通信が中止になった際に実行されます。
  • ontimeout
    タイムアウトエラー時に実行されます。

ProgressEvent

前項の「イベント」項にて、これらのイベントのコールバック関数の引数には ProgressEventオブジェクトが入ります。
xhr.onloadstart = function(event) { 何らかの処理 } のように指定したとしたら、引数のeventです。
ProgressEventオブジェクトのプロパティとして、lengthComputable, loaded, totalが入ります。

  • lengthComputable
    進捗のサイズ計算が可能であるかを取得します。(真偽値)
  • loaded
    これまでに完了した受信バイト数を取得します
  • total
    全体の受信バイト数を取得します

まずはここから
JavaScriptプログラミング講座【XMLHttpRequest について】

addEventListenerを使うとか、ここが参考になりました。
十三章第一回 XMLHTTPRequest – JavaScript初級者から中級者になろう – uhyohyo.net

※その他参考※
JavaScriptプログラミング講座【FormData クラスについて】
Qiita | お疲れさまXMLHttpRequest、こんにちはfetch
jQueryを使わない書き方 ajax, each, trigger, on/off, extend, deferred, animate, css編 | mae’s blog

キリが無いので一旦締めます。。。
また余裕が出てきたときに調べます。

« flexの使い方ネイティヴのJSでイベントを設定する方法 »

Sponsored Link

コメントする

記事の感想や修正依頼等ありましたら、コメントをお願いいたします