不再讓「下集」拖稿了…

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 的動作。

目前有 11 則留言
  1. Avatar hsiang:

    你的JSONP範例跑步起來,少了jsonp: ‘processData’,

  2. Avatar ericsk:

    [quote comment=""]你的JSONP範例跑步起來,少了jsonp: ‘processData’,[/quote]
    多謝指教,已修正 :)

  3. Avatar axis:

    問題回報:

    1.這個範例在IE6跑不起來

    2.在最後要重新顯示#btn的這行
    $(e.target).attr(’disabled’, false);
    已經確定不論在IE還是FIREFOX都沒有作用
    但是改回
    $(’#btn’).attr(’disabled’, false);
    就都正常了

    3.我的jquery版本是 1.2.3 不知是不是有影響??

  4. Avatar ericsk:

    @axis
    感謝回報…不過我身邊沒有 IE6..所以我沒測過我自己的 sample XD
    多謝指正 m(_ _)m

  5. Avatar axis:

    更正一下:

    是在firefox上也跑不起來,但是稍微好一點,至少按送出後
    按鈕有被disable起來,不過之後就無任何反應

    另外我試過了jquery1.1.2版本,結果也是一樣的

  6. Avatar ericsk:

    Orz…
    這…我也不知道您跑不起來的原因是什麼 XD

  7. Avatar axis:

    回覆的這麼快,嚇我一跳

    再補充一下,前一個範例是正常的,
    (除了$(e.target).attr(’disabled’, false);無作用以外)
    是到這一個改用jsonp的方式就不行了

    p.s 教學寫的真好,能寫到讓新手很快入門的,也是一大學問:p

  8. Avatar axis:

    不好意思 又要補充 免的誤導大眾

    在FIREFOX上可以運作成功,但是在IE6上確定不行

    Orz

  9. Avatar Citypig:

    第一個範例的 function showMsg(e) { … }
    在倒數第二行少了 “});” 所以程式會無法正常執行。

  10. Avatar Citypig:

    請問站長:
    要如何才能觸發 jQuery 裡 “error:” 的事件?!
    因為我就算把 msg.php 從 Server 上刪除也不會發生錯誤訊息,只是一直沒結果!!

  11. Avatar ericsk:

    @Citypig
    我覺得會不會是你的 request 有 cache 住了呢?
    應該是 request 送不到(也就是回應不是 2xx)就會 error 了

我要留言
(必填)
(必填)