2019年12月23日 星期一

測試 jQuery 的 Ajax 函數 $.ajax()

最近在改寫個人網站時發現太久沒使用 jQuery 的 Ajax 操作, 很多細節忘記了, 以前在寫 CMS 時忙著趕成果也沒時間整理筆記, 所以上週六 (12/14) 特地到已經很久沒去的左新借了下面幾本書回來複習, 也趁這機會把 Ajax 筆記補起來 :

# 鋒利的 jQuery (單東林, 佳魁)
# 精通 Javascript + jQuery (曾順, 松崗)
# 超詳解 jQuery 開發範例大全 (陳佳新譯, 悅知文化)
# 打造 jQuery 網頁風暴 (張子秋, 佳魁)
# PHP7+MySQL+AJAX 網頁設計範例教本 (陳會安, 旗標)
# PHP 與 MySQL 網頁資料庫程式設計 (陳會安, 松崗)
# jQuery 實戰手冊第三版 (碁峰, 林信良譯, 高科大楠梓分館)

原文書參考 :

# Learning jQuery3 5th Edition (Packt, 2017, by Adam Boduch)
# Begining jQuery 2nd Edition (Apress, 2017, by Jack Franklin)
# jQuery in Action 3rd Edition (Manning, 2015, by Bear Bibeault)
# Javascript & jQuery: the missing manual 3rd Edition (Oreilly, 2014)

其中 missing manual 這本含有豐富的 jQuery UI 主題.

傳統網頁是以同步 HTTP 方式操作, 亦即點超連結換頁或提交表單等動作都會載入新網頁, 等待載入時可能會看到空白頁, 無法像桌面應用程式那樣只更改局部內容, 而且只是更新局部內容卻要重新載入整個網頁也浪費頻寬. 換頁與載入延遲使得網頁介面的使用者經驗不如桌面應用程式. Ajax 技術的出現解決了這個問題, 讓網頁瀏覽器也具有如桌面應用程式類似的效果.

Ajax 全名為 Asynchronous Javascript and XML, 支援 Ajax 的瀏覽器內部實作了Ajax 引擎, 網頁應用程式的 Ajax 呼叫會建立 XMLHttpRequest (XHR) 物件, 將 Ajax 請求轉成 HTTP 請求送給伺服器, 請求發出後程序由 Ajax 引擎在背景中監控, 網頁應用程式仍可繼續往下執行, 等收到伺服器回應時再由回呼函數處理部分網頁內容更新, 因此瀏覽器不會被鎖住以等待伺服端回應, 這種運作方式稱為非同步 HTTP, 參考 :

https://zh.wikipedia.org/zh-tw/AJAX




Ajax 名稱中的 XML 反映了最初作為非同步請求的資料交換格式為 XML, 但並不限於 XML 而已, 也可以用來取得 HTML, JSON, 或 Javascript 檔案, 特別是輕量的 JSON 已取代 XML 成為網頁資料交換的主要格式.

不同瀏覽器的 Ajax 有不相容的問題, 以前須使用額外程式碼逐一判斷用戶瀏覽器類型來建立個別瀏覽器的 XHR 物件, 使 Ajax 操作看起來非常複雜. 所幸 jQuery 的 Ajax 函式庫以統一簡潔的介面解決了跨瀏覽器的 Ajax 呼叫問題, 參考 :

https://api.jquery.com/category/ajax/
https://www.tutorialspoint.com/ajax/ajax_browser_support.htm

jQuery 的 Ajax 函數可分為四類 :


 Ajax 函數分類 說明
 全域 Ajax 事件處理函數 例如 obj.Success(), obj.Error() 等
 幫助函數 (helper) 例如 $.serialize(), $.serializeArray(), 與 $.param() 
 低階介面函數 例如 $.ajax() 與 $.ajaxSetup()
 捷徑函數 (shorthand) 例如 obj.load(), $.get(), $post(), $getJSON(), $getScript()


其中與發送 Ajax 請求相關的函數如下表 :


 Ajax 函數 說明
 $.ajax() 使用 XMLHttpRequest 物件送出 Ajax 請求
 $.ajaxSetup() 設定 Ajax 請求之選項 (Ajax 請求之預設值)
 obj.load() 以 Ajax 請求將遠端文件載入到網頁元件 obj 中
 $.get() 以 HTTP GET 方法送出 Ajax 請求
 $.post() 以 HTTP POST 方法送出 Ajax 請求
 $.getJSON() 以 HTTP GET 方法送出 Ajax 請求來取得伺服端之 JSON 資料
 $.getScript() 以 HTTP GET 方法送出 Ajax 請求來取得伺服端之 Javascript  程式並執行


除 load() 是 jQuery 物件的方法外, 其餘均為 Ajax 全域函數. 其中 load() 方法我之前已經做過完整測試, 參考 :

測試 jQuery 的 Ajax 方法 load()

上表中的 $.ajax() 函數是 jQuery 的 Ajax 技術核心, 是具有較多設定選項的低階函數, 其他函數都是在 $.ajax() 基礎上建構的捷徑函數. 注意, 上面這些方法中, 只有 load() 是物件方法, 必須在某個元件的 jQuery 物件上呼叫, 請求的文件是直接載入於該物件內, 其他都是 jQuery 名稱空間的全域函數, 不須依託物件可直接呼叫. 結構如下所示 :




以下是測試 jQuery 的 CDN 網頁模板 :

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
    <script>
      $(function(){
        //jQuery codes are here
        });
    </script>
  </body>
</html>

此處使用簡寫的 $(function) {}), 原本的寫法是 $(document).ready(function() {}), 表示等待網頁文件載入完成才執行 jQuery 程式碼.


一. $.ajax() 函數的參數選項設定 :

呼叫 $.ajax() 函數須傳入一個選項設定物件 config 當參數 :

$.ajax(config)

設定物件 config 常用屬性如下表 :


 $.ajax() 常用屬性 說明
 url HTTP 請求的目標網址 (字串)
 type HTTP 請求的方式 ("GET" 或 "POST"), 預設為 "GET"
 data 請求附帶的參數資料, 可用物件 {a:1, b: 2} 或字串例如 "a=1&b=2"
 dataType 預期伺服器回應的資料類型字串 (text/html/xml/json/script/jsonp)
 success 請求成功時要執行之回呼函數, function(data, textStatus)
 error 請求失敗時要執行之回呼函數, function(XMLHttpRequest, textStatus)
 complete 請求送出時要執行之回呼函數, function(XMLHttpRequest, textStatus)
 beforeSend 請求送出前要執行之回呼函數, function(XMLHttpRequest)
 cache 是否在記憶體快取此結果 (布林值), 預設為 true 


基本語法例如 :

$.ajx({
  url: "/someURL",
  success: function(data, textStatus) {
    //deal with the
    }
  });

當 Ajax 呼叫成功時就執行 success 所指定之回呼函數. $.ajax() 的傳回值為一個 jqXHR 物件, success 回呼函數也可以不寫在 config 屬性, 而是傳給 jqXHR 物件的 done() 方法, 例如 :

var jqxhr=$.ajx({url: "/someURL"});
jqxhr.done(function(data, textStatus) {
    //deal with the
    });

參考 :

https://api.jquery.com/jquery.ajax/

屬性 url 是 Ajax 請求資源之 URL, 主要有五種資源 (對應 MIME 類型) :


 檔案資源 說明 Content-Type
 html  半結構化資料, 毋須走訪, 直接插入網頁中 text/html
 xml 結構化資料, 須走訪 (重量級可重用資料) text/xml
 json 結構化資料, 須走訪 (輕量級可重用資料) application/json
 script 載入後自動執行的程式碼 (非資料) text/javascript
 text 非結構化資料, 毋須走訪, 直接插入網頁中 text/plain


注意, 後端程式應該在回應標頭填入回應資料的 MIME 類型, 輸出標頭函數 header() 要放在最前面, 以 PHP 回應 XML 資料為例 :

<?php
header("Content-Type: text/xml; charset: utf-8");
.....

如果回應資料是 HTML 格式, 則標頭函數 header() 要這麼寫 :

<?php
header("Content-Type: text/html; charset: utf-8");
.....

如果回應資料是 JSON 格式, 則標頭函數 header() 要這麼寫 :

<?php
header("Content-Type: application/json; charset: utf-8");

如果回應資料是 Javascript 程式, 則標頭函數 header() 要這麼寫 :

<?php
header("Content-Type: application/javascript; charset: utf-8");

$.ajax() 設定物件屬性 type 為 Ajax 請求所使用之 HTTP 方法, 可用 "GET 或 POST, 預設為 GET, 也可用其他方法如 DELETE, 但要看伺服器是否支持.

屬性 data 為提出 Ajax 請求時要傳送給伺服端的參數資料, 可用 Javascript 物件實體例如 {a: 1, b: 2} 或字串例如 "a=1&b=2". 如果 data 是物件, 則 jQuery 將一律以 POST 方式發出請求, 即使 type 屬性設為 GET 也會如此; 若 data 為字串, 則 jQuery 將以 GET 方式發出請求 (PHP 程式要用 $_GET 讀取參數). 若 url 屬性中有帶參數, data 屬性又帶物件參數, 這種混合情況 jQuery 會以 POST 方式發出請求.

屬性 dataType 是預期伺服端回應資料之類型, 應該與伺服端應用程式 HTTP 回應的 MIME 對應, 若無設定會以伺服器回應中的 MIME 類型來解析, 並將解析結果以參數 responseText 或 responseXML 傳遞給回呼函數.

屬性 cache 為布林值, 用來設定是否要將回應結果快取在記憶體中, 預設為 true, 亦即要求相同的 URL 時會優先從 Cache 記憶體中取得, 如果要每次都取得伺服上最新資料, 要將 cache 設為 false.

為了測試 HTML 文件載入, 先準備一個 test.htm 檔案如下 :

<p style='font-weight:bold;'>Hello World!</p>
<p id='normal'>Hello World!</p>

因為 Ajax 載入的只是要嵌入的網頁片段而已, 因此不需要完整的網頁結構 (即不需要 html, head, body 等標籤).

以下範例主要測試 url, type, 以及 data 這三個屬性之設定 :


範例 1 : $.ajax() 載入 HTML 網頁 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <div id="result">This is a div container</div>
  <input type="button" id="load" value="load">
  <script> 
    $(function(){
      $("#load").click(function() {
        $.ajax({
          type: "GET",
          url: "test.htm",
          dataType: "html",
          success: function(data) {
            $("#result").html(data);
            },
          error: function(xhr) {
            alert(xhr.status);
            }     
          });
        });
      });
  </script>
  </body>
</html>

此例沒有攜帶 data 參數, 當按下 Load 按鈕後以 GET 方法取得 text.htm 檔內容並插入 id 為 result 的元素裡, 結果如下 :




接下來要測試帶參數情況, 需在後端準備一個 PHP 程式 test.php 如下 :

<?php
echo "<p>get :user=".$_GET["user"].", pwd=".$_GET["pwd"]."</p>".
     "<p>post : user=".$_POST["user"].", pwd=".$_POST["pwd"]."</p>".
     "<p style='font-weight:bold;'>Hello World!</p>".
     "<p id='normal'>Hello World!</p>";
?>

此程式同時使用 $_GET[] 與 $_POST[] 這兩個全域函數讀取 HTTP 請求所傳遞的 user 與 pwd 參數, 目的是要看看不同的參數傳遞方式要用哪種函數才能讀取.


範例 2 : $.ajax() 帶 URL 查詢字串載入 PHP 網頁 (POST) [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <div id="result">This is a div container</div>
  <input type="button" id="load" value="load">
  <script> 
    $(function(){
      $("#load").click(function() {
        $.ajax({
          type: "POST",
          url: "test.php?user=tony&pwd=123",
          dataType: "html",
          success: function(data) {
            $("#result").html(data);
            },
          error: function(xhr) {
            alert(xhr.status);
            }     
          });
        });
      });
  </script>
  </body>
</html>

此例沒有設定 data 參數, 而是將參數以查詢字串附在 URL 後面, 所以 Ajax 引擎會以 POST 方法發出請求, 但是因為參數以查詢字串方式傳送, 因此後端必須以 $_GET[], 可在 Chrome 按 F12 檢查 Network 頁籤中的 HTTP 訊息, 結果如下 :





可見雖然 HTTP 請求是用 POST 方法傳送 (type 屬性設為 POST), 但因為參數是附在 URL 後面的查詢字串方式傳送 (url 屬性值後面 ? 所帶參數), 後端用 $_POST[] 是讀不到參數的, 要用 $_GET[] 函數才讀得到.

此例若 type 屬性改為 GET, 則參數理所當然還是用查詢字串送出, 結果只有 HTTP 方法變成 GET, 其餘結果都一樣, 參數要用 $_GET[] 函數才讀得到.


範例 3 : $.ajax() 帶 data 物件參數載入 PHP 網頁 (GET) [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <div id="result">This is a div container</div>
  <input type="button" id="load" value="load">
  <script> 
    $(function(){
      $("#load").click(function() {
        $.ajax({
          type: "GET",
          url: "test.php",
          dataType: "html",
          data: {user: "tony", pwd: 123},
          success: function(data) {
            $("#result").html(data);
            },
          error: function(xhr) {
            alert(xhr.status);
            }     
          });
        });
      });
  </script>
  </body>
</html>

此例與上面範例 2 相反, type 屬性設定用 GET 方法, data 屬性值為參數物件, 所以 Ajax 引擎會把 data 的參數物件轉成查詢字串放在 URL 後面送出去, 除了 HTTP 請求改為 GET 外, 結果與上面範例 2 完全一樣, 後端 PHP 程式也是要用 $_GET[] 函數才能取得參數. 結果如下 :





如果將上面範例 3 的 type 屬性改為 POST, 則 HTTP 請求就會是 POST, 那麼 data 屬性值的參數物件就會被放在 HTTP 訊息的 BODY 中傳送, 不會被轉成查詢字串, 而後端要用 $_POST[] 捕捉參數, 如下面範例 4 所示 :


範例 4 : $.ajax() 帶 data 物件參數載入 PHP 網頁 (POST) [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <div id="result">This is a div container</div>
  <input type="button" id="load" value="load">
  <script> 
    $(function(){
      $("#load").click(function() {
        $.ajax({
          type: "POST",
          url: "test.php",
          dataType: "html",
          data: {user: "tony", pwd: 123},
          success: function(data) {
            $("#result").html(data);
            },
          error: function(xhr) {
            alert(xhr.status);
            }     
          });
        });
      });
  </script>
  </body>
</html>

此例 type 為 POST, data 為參數物件, HTTP 請求為 POST, 所以後端要用 $_POST[] 才能取得參數, 結果如下 :






檢視 HTTP 訊息可知 HTTP 請求為 POST 方法, 參數放在 body 的 form data 中傳送, 後端 PHP 要用 $_POST[] 函數才能捕捉到參數.


範例 5 : $.ajax() 帶混合參數載入 PHP 網頁 (POST) [看原始碼]


<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <div id="result">This is a div container</div>
  <input type="button" id="load" value="load">
  <script> 
    $(function(){
      $("#load").click(function() {
        $.ajax({
          type: "POST",
          url: "test.php?user=tony",
          dataType: "html",
          data: {pwd: 123},
          success: function(data) {
            $("#result").html(data);
            },
          error: function(xhr) {
            alert(xhr.status);
            }     
          });
        });
      });
  </script>
  </body>
</html>

此例 type 屬性為 POST, 兩個參數一個放在 URL 查詢字串 (user=tony), 另一個以物件 {pwd: 123} 放在 data 屬性裡, 結果 HTTP 請求以 POST 方法傳送, 但參數一個會放在 URL 查詢字串, 另一個則放在 body 的 fform-data 裡傳送, 結果如下 :




可見以查詢字串傳送的參數 user 要用 $_GET[] 函數才抓得到; 而用物件表示的 pwd 參數則要用 $_POST[] 函數才抓得到, 檢視 HTTP 訊息即可驗證此結果 :




如果將上面範例 5 的 type 屬性改成 GET, 參數仍以查詢字串與 data 物件混合方式送出, 則 data 物件所攜帶的參數會因為 type=GET 而被強制改為查詢字串送出, 所以後端 PHP 要用 $_GET[] 函數才能捕捉到參數 :


範例 6 : $.ajax() 帶混合參數載入 PHP 網頁 (GET) [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <div id="result">This is a div container</div>
  <input type="button" id="load" value="load">
  <script> 
    $(function(){
      $("#load").click(function() {
        $.ajax({
          type: "GET",
          url: "test.php?user=tony",
          dataType: "html",
          data: {pwd: 123},
          success: function(data) {
            $("#result").html(data);
            },
          error: function(xhr) {
            alert(xhr.status);
            }     
          });
        });
      });
  </script>
  </body>
</html>

此例與上面範例 5 差別只是 type 改為 GET 而已, 結果如下 :





可見 data 裡的參數物件被強制轉成查詢字串附在 URL 後面傳送了.


範例 7 : $.ajax() 帶 data 查詢字串載入 PHP 網頁 (GET) [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <div id="result">This is a div container</div>
  <input type="button" id="load" value="load">
  <script> 
    $(function(){
      $("#load").click(function() {
        $.ajax({
          type: "GET",
          url: "test.php",
          dataType: "html",
          data: "user=tony&pwd=123",
          success: function(data) {
            $("#result").html(data);
            },
          error: function(xhr) {
            alert(xhr.status);
            }     
          });
        });
      });
  </script>
  </body>
</html>

此例 type 為 GET, data 為查詢字串, HTTP 請求為 GET 方法, data 所帶參數在後端要用 $_GET[] 才能讀取, 可見 data 屬性值是以查詢字串送出, 結果如下 :





如果將上面範例 7 的 type 改為 POST


範例 8 : $.ajax() 帶 data 查詢字串載入 PHP 網頁 (POST) [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <div id="result">This is a div container</div>
  <input type="button" id="load" value="load">
  <script> 
    $(function(){
      $("#load").click(function() {
        $.ajax({
          type: "POST",
          url: "test.php",
          dataType: "html",
          data: "user=tony&pwd=123",
          success: function(data) {
            $("#result").html(data);
            },
          error: function(xhr) {
            alert(xhr.status);
            }     
          });
        });
      });
  </script>
  </body>
</html>


此例只是把範例 7 的 type 改成 POST 而已, 可見 data 的查詢字串被放在 HTTP 的 BODY 中傳送, 而非 URL 的查詢字串中, 結果如下 :





最後一個組合是上面範例 5 的變形, 即 type 設為 POST, 傳送參數均為查詢字串, 但部分參數放在 url 查詢字串, 部分放在 data 查詢字串的情況, 如下面範例 9 所示 :


範例 9 : $.ajax() 帶 url 與 data 查詢字串載入 PHP 網頁 (POST) [看原始碼]


<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <div id="result">This is a div container</div>
  <input type="button" id="load" value="load">
  <script> 
    $(function(){
      $("#load").click(function() {
        $.ajax({
          type: "POST",
          url: "test.php?user=tony",
          dataType: "html",
          data: "pwd=123",
          success: function(data) {
            $("#result").html(data);
            },
          error: function(xhr) {
            alert(xhr.status);
            }     
          });
        });
      });
  </script>
  </body>
</html>

此例所傳送的參數均為查詢字串, 一個放在 url 中, 一個放在 data 屬性中, 雖然 HTTP 以 POST 方法送出, 但只有 data 所帶字串放在 HTTP 的 BODY 中送出 (form-data), url 所帶參數則以查詢字串附在 URL 中送出, 結果如下 :





綜合上面測試可知, Ajax 呼叫的 type 屬性決定了最後送出 HTTP 請求所使用的方法是 GET 還是 POST, 但後端 PHP 程式要用 $_GET[] 還是 $_POST[] 捕捉參數則還要看參數是怎麼攜帶的 :
  • 若 type 是 GET, 不論參數是放在 url 查詢字串還是 data 屬性中, 一律轉成查詢字串附在 URL 中傳遞, 後端須用 $_GET[] 讀取. 
  • 若 type 是 POST, 則放在 url 屬性中的參數以查詢字串附在 URL 中傳遞, 後端須以 $_GET[] 讀取; 放在 data 的參數一律放在 HTTP 的 BODY 中傳遞, 後端須以 $_POST[] 讀取.  
測試結果摘要如下表 :


 type 屬性 url 屬性 data 屬性 HTTP 請求 後端 PHP 讀取參數
 GET test.php?a=1&b=2 無 GET $_GET["a"] 與 $_GET["b"]
 POST test.php?a=1&b=2 無 POST $_GET["a"] 與 $_GET["b"]
 GET test.php {a:1, b: 2} GET $_GET["a"] 與 $_GET["b"]
 POST test.php {a:1, b: 2} POST $_POST["a"] 與 $_POST["b"]
 GET test.php?a=1 {b: 2} GET $_GET["a"] 與 $_GET["b"]
 POST test.php?a=1 {b: 2} POST $_GET["a"] 與 $_POST["b"]
 GET test.php "a=1&b=2" GET $_GET["a"] 與 $_GET["b"]
 POST test.php "a=1&b=2" POST $_POST["a"] 與 $_POST["b"]
 POST test.php?a=1 "b=2" POST $_GET["a"] 與 $_POST["b"]


以上測試之目的只是要探究不同的參數指定方式究竟 jQuery 的 Ajax 引擎會如何處理而已, 其實不論參數是放在 HTTP 請求的標頭 (GET 查詢字串), 還是放在 BODY (POST 表單資料) 中, PHP 有一個萬用的 $_REQUEST[] 函數都可以捕捉到全部參數.

為了測試此功能, 將上面所用的後端程式 test.php 修改為如下的 test_request.php :

<?php
echo "<p>get :user=".$_GET['user'].", pwd=".$_GET['pwd']."</p>".
     "<p>post : user=".$_POST['user'].", pwd=".$_POST['pwd']."</p>".
     "<p>post : user=".$_REQUEST['user'].", pwd=".$_REQUEST['pwd']."</p>".
     "<p style='font-weight:bold;'>Hello World!</p>".
     "<p id='normal'>Hello World!</p>";
?>

然後將上面範例 9 的 url 屬性改為 "test_request.php?user=tony", 其餘不變 :


範例 10 : $.ajax() 使用 $_REQUEST[] 捕捉參數 [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <div id="result">This is a div container</div>
  <input type="button" id="load" value="load">
  <script> 
    $(function(){
      $("#load").click(function() {
        $.ajax({
          type: "POST",
          url: "test_request.php?user=tony",
          dataType: "html",
          data: "pwd=123",
          success: function(data) {
            $("#result").html(data);
            },
          error: function(xhr) {
            alert(xhr.status);
            }     
          });
        });
      });
  </script>
  </body>
</html>

結果如下 :





可見兩個須分別用 $_GET[] 與 $_POST[] 擷取的參數只要用 $_REQUEST[]就可以完整捕捉到, 不需要去傷腦筋到底要用 $_GET[] 還是 $_POST[].


二.  Ajax 請求 XML 資料 : 

以上範例為了測試方便, 都是直接在 url 與 data 屬性中直接給定參數值, 實際的網頁應用是以表單元件來輸入, 例如系統登入功能常須輸入帳號與密碼, 利用 Ajax 將登入資訊以非同步方式傳遞給後端驗證, 結果以 XML 或 JSON 格式資料回應, Ajax 應用程式解析回應資料後局部更改網頁內容, 不需要重新載入網頁即可完成 HTTP 請求與回應作業.

下列範例 11 測試後端 PHP 程式回應 XML 格式資料的做法, 前端網頁為系統登入畫面, 輸入帳號密碼按登入鈕後送出 Ajax 請求, 為了簡單起見, 後端不做資料庫讀取驗證, 直接將帳密以 XML 格式打包後回應, 前端網頁解析 XML 資料後在網頁中顯示登入資訊. 為此準備如下後端程式 test_xml.php :

<?php
header("Content-Type: text/xml");
$user=$_POST["user"];           
$pwd=$_POST["pwd"];
$response="<?xml version='1.0' ?>".
          "<result>".
          "<user>".$user."</user>".
          "<pwd>".$pwd."</pwd>".
          "</result>";
echo $response;
?>

此後端程式首先須用 header() 設定回應標頭之 Content-Type 為 XML 資料


範例 11 : $.ajax() 傳送表單參數 (XML 回應) [PHP 原始碼] [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <form>
   <label for="user">帳號 : </label>
   <input type="text" id="user"><br>
   <label for="pwd">密碼 : </label>
   <input type="text" id="pwd">
   <button id="login">登入</button>
  </form>
  <div id="result"></div>
  <script> 
    $(function(){
      $("#login").click(function(e){
        e.preventDefault();
        $.ajax({
          type: "POST",
          url: "test_xml.php",
          data: {user: $("#user").val(), pwd: $("#pwd").val()},
          success: function(data) {
            var user=$(data).find("user").text();
            var pwd=$(data).find("pwd").text();
            var result="帳號=" + user + " 密碼=" + pwd;
            $("#result").html(result);
            },
          error: function(xhr) {
            alert(xhr.status);
            }     
          });
        });
      });
  </script>
  </body>
</html>

此例傳入 success 回呼函數的參數 data 即 Ajax 回應之 XML 資料, 利用 $() 將其轉成 jQuery 物件後即可用 find() 方法搜尋其中的 user 與 pwd 元素, 再呼叫 text() 方法取出 XML 元素之內文. 結果如下 :





檢查回應訊息, 可見 XML 回應資料 :




注意, 上例中因為表單 form 元件內的按鈕預設動作會提交表單, 這會造成網頁重新載入而使 Ajax 破功, 因此必須利用 click 事件的 preventDefault() 方法來阻止預設之表單提交動作 (去除 form 元素就不需要這麼做了), 參考 :

停止表單提交根據Ajax響應

另外一個阻止表單預設提交動作的方法是在 click 事件的回呼函數最後回傳 false 給 click() 即可, 這樣 click 事件的回呼函數就不需要傳入事件物件 e 了.  如下面範例所示 :


範例 12 : 阻止表單提交方法 2 (XML 回應) [PHP 原始碼] [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <form>
   <label for="user">帳號 : </label>
   <input type="text" id="user"><br>
   <label for="pwd">密碼 : </label>
   <input type="text" id="pwd">
   <button id="login">登入</button>
  </form>
  <div id="result"></div>
  <script> 
    $(function(){
      $("#login").click(function(){
        $.ajax({
          type: "POST",
          url: "test_xml.php",
          data: {user: $("#user").val(), pwd: $("#pwd").val()},
          success: function(data) {
            var user=$(data).find("user").text();
            var pwd=$(data).find("pwd").text();
            var result="帳號=" + user + " 密碼=" + pwd;
            $("#result").html(result);
            },
          error: function(xhr) {
            alert(xhr.status);
            }     
          });
        return false;
        });
      });
  </script>
  </body>
</html>

此例與範例 11 執行結果一樣.

在上面範例 11 與 12 中使用 jQuery 選擇器逐一將表單參數填入 data 屬性中, 如果表單欄位很多就很麻煩且容易打錯, 這可以利用 jQuery 表單物件的 serialize() 方法來從表單控制項自動建立查詢字串給 $.ajax() 的 data 屬性, 不需要一一處理每一個欄位.

serialize() 方法是專為表單元件 form 設計, 它會收集有效的表單控制項, 將其值以 URL 編碼方式格式化後傳回查詢字串, 可直接作為 $.ajax() 的 data 屬性值傳給伺服端. serialize() 是控制項集合的方法, 可以透過表單控制項選擇器產生集合物件, 例如 :

var queryStr=$(":radio, :checkbox").serialize();

但更常見是直接用表單元件 form 的 id 選取整個表單, 例如 :

var queryStr=$("#form_id").serialize();

這樣它會收集表單內全部有效的控制項之值並編碼為如下格式字串 :

user=tony&pwd=123

 注意, 所謂有效的控制項不包含下列元件 :
  • 未選取的選項元件 (radio 與 checkbox)
  • 未選取的下拉式選單 (select option)
  • 停用 (disabled) 的控制項
這些控制項不會被 serialize() 收集.

另外還有一個常被忽略之處是, 表單內的控制項必須有 name 屬性, 這樣 serialize() 才知道該控制項要送出的變數名稱是甚麼.


範例 13 : 用 serialize() 產生查詢字串 (XML) [PHP 原始碼] [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <form id="login_form">
   <label for="user">帳號 : </label>
   <input type="text" id="user" name="user"><br>
   <label for="pwd">密碼 : </label>
   <input type="text" id="pwd" name="pwd">
   <button id="login">登入</button>
  </form>
  <div id="result"></div>
  <script> 
    $(function(){
      $("#login").click(function(){
        alert($("#login_form").serialize());
        $.ajax({
          type: "POST",
          url: "test_xml.php",
          data: $("#login_form").serialize(),
          success: function(data) {
            var user=$(data).find("user").text();
            var pwd=$(data).find("pwd").text();
            var result="帳號=" + user + " 密碼=" + pwd;
            $("#result").html(result);
            },
          error: function(xhr) {
            alert(xhr.status);
            }     
          });
        return false;
        });
      });
  </script>
  </body>
</html>

此例在 click 事件處理函數第一行用 alert() 顯示序列化結果如下 :







三.  Ajax 請求 JSON 資料 : 

伺服端除了可回應 XML 資料外, 現在更常見的是回應 JSON, 這種資料格式比 XML 更小更簡潔, 可加快回應速度. JSON 是 Javascript 物件格式, 因此在客戶端可像物件一樣直接用點運算子來擷取屬性值, 處理起來比 XML 更方便, 例如回應的 JSON 如下時, 便可用 data.user 取得 "tony", 用 data.pwd 取得 "123" :

{"user":"tony","pwd":"123"}

而後端 PHP 程式可用 encode_json() 函數可將陣列轉成 JSON 格式之資料 :

$response=array();
$response[0]=array(
             'user' => 'tony',
             'pwd'=> '123'
             );
echo json_encode($response);

參考 :

Returning JSON from a PHP Script

為了測試回應 JSON 資料, 準備了如下之 PHP 程式 test_jsom.php :

<?php
header("Content-Type: application/json");
$user=$_POST["user"];           
$pwd=$_POST["pwd"];
$response=array(
  'user' => 'tony',
  'pwd' => '123'
  );
echo json_encode($response);
?>

此處只是將收到的參數簡單地組成一維陣列, 再用 json_encode() 轉成 JSON 格式回應給客戶端而已. 


範例 14 : $.ajax() 傳送表單參數 (JSON 回應) [PHP 原始碼] [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <form id="login_form">
   <label for="user">帳號 : </label>
   <input type="text" id="user" name="user"><br>
   <label for="pwd">密碼 : </label>
   <input type="text" id="pwd" name="pwd">
   <button id="login">登入</button>
  </form>
  <div id="result"></div>
  <script> 
    $(function(){
      $("#login").click(function(){
        $.ajax({
          type: "POST",
          url: "test_json.php",
          data: $("#login_form").serialize(),
          success: function(data) {
            var user=data.user;
            var pwd=data.pwd;
            var result="帳號=" + user + " 密碼=" + pwd;
            $("#result").html(result);
            },
          error: function(xhr) {
            alert(xhr.status);
            }     
          });
        return false;
        });
      });
  </script>
  </body>
</html>

結果如下 :





查看回應訊息可看到伺服端回應一個 JSON 格式字串 :




如果傳回的資料是二維陣列資料, 例如一張表, 則客戶端收到回應後須要使用迴圈來剖析 JSON 資料, 參考 :

# $.ajax 存取 Json 簡單範例

下面範例模擬後端回應以 JSON 格式表示的國英數成績單 (此處省略查詢資料的部分), 成績單是二維資料可用二維陣列表示, 為此準備了一個 PHP 程式 test_json2.php 如下 :

<?php
header("Content-Type: application/json");
$response=array(
  'S01' => Array('English' => 78, 'Chinese' => 89, 'Math' => 65),
  'S02' => Array('English' => 98, 'Chinese' => 56, 'Math' => 79),
  'S03' => Array('English' => 66, 'Chinese' => 89, 'Math' => 87),
  );
echo json_encode($response);
?>

放在二維陣列中的成績單傳給 json_encode() 後會轉成如下 JSON 資料 : 

{"S01":{"English":78,"Chinese":89,"Math":65},"S02":{"English":98,"Chinese":56,"Math":79},"S03":{"English":66,"Chinese":89,"Math":87}}

一共有三筆學生成績, 客戶端收到 JSON 回應後, 須使用迴圈迭代來逐一取出每筆資料.


範例 14 : 回應二維 JSON 資料 [PHP 原始碼] [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <form">
   <button id="login">載入</button>
  </form>
  <div id="result"></div>
  <script> 
    $(function(){
      $("#login").click(function(){
        $.ajax({
          type: "POST",
          url: "test_json2.php",
          success: function(data) {
            var i=0, arr=[];
            $.each(data, function (k, v){
              arr[i]=k + ' ' + v.English + ' ' + v.Chinese + ' ' + v.Math;
              ++i;
              });
            $("#result").html(arr.join('<br>'));
            },
          error: function(xhr) {
            alert(xhr.status);
            }     
          });
        return false;
        });
      });
  </script>
  </body>
</html>

此例因為 Ajax 請求回應值為 JSON 物件, 因此使用 Ajax 的公用函數 $.each() 來迭代物件中的屬性, 傳入回呼函數的兩個參數 k, v 分別是 JSON 物件中的鍵與值, 下一層的值可用 v.鍵 或 v[鍵] 取得, 結果如下 :




按 "載入" 鈕後即可在下方看到回應之成績單資料, 並未重新載入網頁. 下面範例 16 則是改用 v[鍵] 存取迭代之屬性值 :


範例 16 : 回應二維 JSON 資料 [PHP 原始碼] [看原始碼]

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
    </style>
  </head>
  <body>
  <form">
   <button id="login">載入</button>
  </form>
  <div id="result"></div>
  <script> 
    $(function(){
      $("#login").click(function(){
        $.ajax({
          type: "POST",
          url: "test_json2.php",
          success: function(data) {
            var i=0, arr=[];
            $.each(data, function (k, v){
              arr[i]=k + ' ' + v['English'] + ' ' + v['Chinese'] + ' ' + v['Math'];
              ++i;
              });
            $("#result").html(arr.join('<br>'));
            },
          error: function(xhr) {
            alert(xhr.status);
            }     
          });
        return false;
        });
      });
  </script>
  </body>
</html>

結果與上面範例 15 完全一樣.

參考 :

AJAX JavaScript 與 jQuery 教學範例 for PHP

沒有留言 :