不再讓「下集」拖稿了…

jQuery.ajax() 的 callback

jQuery 提供的 ajax 函式其中有 4 個 callbacks: beforeSend, complete, error, 以及 success,當一個 Ajax request 送出到完成,會依照:

beforeSend » success or error » complete

這樣的順序來呼叫你所定義的 callback 函式,也就是不管成功或者失敗,beforeSendcomplete 定義的 callbacks 都會被呼叫到,而 successerror 的 callback 則是視 Ajax request 的成敗來決定誰會被呼叫。

另外值得一提的是,jQuery 1.2 開始加入 JSONP 的支援,當你的 dataType 設成 jsonp 的時候,jQuery 的 ajax 函式會將 dataType 改為 script 並且多送一個 callback 參數到 server,然後 server 可以利用 callback 參數的值來輸出適當的 script 送回給 browser 來執行,也許這樣講很抽象,直接來看之前的例子改成 JSONP 的作法:

msg.php

<?php
  $res = "Hello, ";

  if (!strcmp($_GET['gender'], "Male")) {
    $res .= "Mr. ";
  } else {
    $res .= "Miss ";
  }

  $res .= $_GET['name'];

  echo $_GET['callback']."({msg: '$res'});";
?>

然後 JavaScript 的部份改成:

function processData(data) {
  $('#msg').html(data.msg);
  $('#msg').fadeIn();
  setTimeout(function(){
    $('#msg').fadeOut();
    $(e.target).attr('disabled', false);
  }, 3000);
}

function showMsg(e) {
  $(e.target).attr('disabled', true);
  $.ajax({
    url: 'msg.php',
    dataType: 'jsonp',
    jsonp: 'processData',
    data: {gender: $('#gender').val(), name: $('#name').val(), callback: 'processData'},
    error: function(xhr) {
      alert('Ajax request 發生錯誤');
      $(e.target).attr('disabled', false);
    }
}

先看 msg.php 最後的輸出,其實是根據 callback 參數的值來決定,以這個例子而言可能就會輸出

processData({msg: 'Hello, Mr. XXX'});

所以 jQuery 會自動幫你把 dataType 設成 script 好讓 browser 認得要執行,這樣就不會去呼叫 successcomplete 的 callback ,而是去呼叫你自己定義的 callback function 。

JSONP 的應用愈來愈多,像是 Google Calendar API 或是 twitter API 都有用到 JSONP,如果想了解更深入一點,可以參考這裡

Global event

(在看這一段之前,要先認識 jQuery event 的 bind/trigger 才比較能瞭解)

當 Ajax request 產生時,如果你希望網頁上其它的元件能根據 Ajax 進行中不同的流程而做些動作的話,jQuery 已經定義好了一組 event handlers (見文件中的 ajaxXXXX)。而在 jQuery.ajax 函式中,如果 global 參數設為 true 的時候(預設就是 true),它會在 ajax 的流程中,適時去 trigger 這些 event,如果有網頁元件使用這些函式來 bind 住這些 event 的話,就會去呼叫設定的 callback function。而它 trigger 的順序為:

ajaxStart » ajaxSend » ajaxSuccess or ajaxError » ajaxComplete » ajaxStop

假設我們在按鈕旁加上一個 loading… 的圖案,預設是不顯示,但是當 ajaxStart 的時候把它打開,而在 ajaxComplete 的時候再關閉。先在按鈕後加上:

<img src="spinner.gif" style="display:none" id="loading" />

然後在 script 中幫 #loading 去 bind 必要的 event:

$(document).ready(function(e){
  $('#btn').click(showMsg);
  $('#loading').ajaxStart(function(e){
    $(e.target).show();
  });
  $('#loading').ajaxComplete(function(e){
    $(e.target).hide();
  });
});

這樣一來,當按鈕按下後建立一個 Ajax request 後,所有 bind ajaxStart event 的元件就會去呼叫定義好的 callback,而整個 Ajax request 完成後,ajaxComplete event 又會被 trigger。

jQuery/Ajax 部份所提供的 API 真是讓我們減輕不少使用 Ajax 上的負擔,再加上自訂 ajaxXXXX event 的架構,讓你不用把所有的動作擠在同一個 callback function 之中,完全符合 jQuery 的以網頁元件為中心的理念。

如果不想每一次使用 ajax 都設定那麼多參數的話,其實 jQuery/Ajax API 還定義了一些快速的函式(如:load, post, getJSON, getScript),一樣是兩三下就完成 ajax 的動作。