Ajax JavaScript 與 jQuery 教學範例 for PHP

使用 JavaScript 與 jQuery 兩種方式搭配 PHP,來各別實作 Ajax 應用,並解說 Ajax 的原理,與 HTTP request、response 的 headers、body 內容解說,以及實現 Ajax 的 XMLHttpRequest 物件。

ajax-javascript-jquery-example-for-php_00

簡介

什麼是 Ajax

Ajax(Asynchronous JavaScript and XML,非同步的 JavaScript 與 XML 技術)並不是一種程式語言,而是一種不需重新載入整個網頁的情況下,能夠更新部分網頁,綜合了「伺服」(如 PHP)與「客戶」(JavaScript)語言的瀏覽器端網頁開發技術。

表單(form)vs Ajax

表單 – 發送 HTTP 請求

早期傳統的 Web 應用,通常都是使用表單(form)向伺服器發送一個 HTTP 請求,伺服器接收處理傳來的表單並送回一個新的網頁,但前後兩個頁面的大部份 HTML 碼通常僅有約 5% 是變動的資料,這種做法浪費了許多頻寬。

想要更新內容或者提交一個表單,必須重新載入整個網頁

Ajax – 發送 HTTP 請求

Ajax 應用可以僅向伺服器傳送並取回指定資料,接著在客戶端使用 JavaScript 處理伺服器回應的資料,因只取須要的資料,所以伺服器回應更快且負荷也減少了。

使用 JavaScript 即時與伺服器進行少量資料交換,來非同步局部更新網頁

Ajax 運作原理

  1. 使用者在「瀏覽器」觸發一個事件,例如點擊按鈕
  2. 將上述獲的事件的同時,使用 JavaScript 的 XMLHttpRequest 物件,在背景對「Web 伺服器」發送一個 HTTP 請求,達到與「Web 伺服器」進行資料的非同步交換
  3. 將從「Web 伺服器」取得的資料,使用 JavaScript 操作 DOM,來實現動態局部更新「瀏覽器」的網頁內容

HTTP

完整的 HTTP 流程

一個完整的 HTTP 流程,通常有下面七個步驟:

  1. 建立 TCP 連接
  2. 瀏覽器:向「Web 伺服器」發送請求命令
  3. 瀏覽器:發送請求表頭(headers)
  4. Web 伺服器:回應(response)
  5. Web 伺服器:發送回應(response)資訊
  6. Web 伺服器:向「瀏覽器」發送資料
  7. Web 伺服器:關閉 TCP 連接

HTTP 請求方法(GET 與 POST)

GET

傳遞參數(name=value)都在 URL 中,任何人都可見
  • 適用於資訊獲取(查詢結果)
  • 使用 URL 傳遞參數(name=value)
  • 對所發送資訊的數量有限制,一般在 2,000 個字串

POST

傳遞參數(name=value)不可見
  • 適用於修改伺服器上的資料
  • 所有傳遞的參數(name=value)將被嵌入 HTTP 的請求主體(body)
  • 對所發送資訊的數量無限制

HTTP 請求(request)

一個 HTTP 請求由四個部分構成:

  1. HTTP 請求方法(method)或「動詞」(verb),比如是 GET 或 POST 請求
  2. 要請求的 URL,就是請求的網址
  3. 一組選擇性的請求表頭(headers),包含一些「客戶端環境」與「身份驗證」資訊……
  4. 一個選擇性的請求主體(body),也就是請求正文,可包含客戶提交的「查詢字串」或「表單」資訊……

HTTP 請求內容

  • 請求方法:GET
  • 請求地址:/service.php?number=1020501
  • HTTP 版本:HTTP/1.1
  • 請求表頭:紅框部分
  • 請求主體:綠框部分
請求「表頭」與「主體」之間有一個空行,用來表示請求「表頭」已經結束了,接下來是請求「主體」
GET /service.php?number=1020501 HTTP/1.1
Host: localhost Connection: keep-alive User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.99 Safari/537.36 q=0.01 Accept: */* Referer: http://localhost/javascript-demo.html Accept-Encoding: gzip, deflate, sdch Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
number=1020501

HTTP 回應(response)

伺服器的 HTTP 回應(response)有三個部分:

  1. 由數值與文字所組成的狀態碼(status code),指示請求成功或失敗
  2. 一組回應表頭(headers)包含許多有用的資訊,例如伺服器類型、日期時間、內容類型和長度……
  3. 回應主體(body)也就是回應正文,例如「文字字串」或「HTML 代碼」……

HTTP 回應內容

  • HTTP 版本:HTTP/1.1
  • 回應狀態碼:200 OK,代表請求成功
  • 回應資料類型:application/json; charset=UTF-8,代表資料類型 JSON;編碼格式 UTF-8
  • 回應表頭:紅框部分
  • 回應主體:綠框部分(這裡回應的資料格式為 JSON
回應「表頭」與「主體」之間有一個空行,用來表示回應「表頭」已經結束了,接下來是回應「主體」
HTTP/1.1 200 OK
Date: Sat, 26 Sep 2015 06:12:28 GMT Server: Apache/2.4.12 (Win32) OpenSSL/1.0.1l PHP/5.6.8 X-Powered-By: PHP/5.6.8 Content-Length: 111 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: application/json; charset=UTF-8
{"number":"1020501","name":"\u738b\u4e00\u5091","sex":"\u7537"}

HTTP 狀態碼(status code)

HTTP 狀態碼由 3 位數字組成,第一個位數定義了回應的物件(後兩位數沒有代表任何具體的分類)

  • 1xx:指示資訊。表示收到 Web 瀏覽器請求,正在進一步的處理中
  • 2xx:成功。表示請求已經被正確接收,並已經被理解和接受,例如:200 OK
  • 3xx:重新導向。表示請求沒有成功,必須採取進一步的動作以完成請求
  • 4xx:客戶端錯誤。表示客戶端提交的請求中有錯誤或者不能被完成,例如:404 NOT found,代表請求中引用的檔案不存在
  • 5xx:伺服器錯誤。表示伺服器不能完成請求的處理,儘管請求是正確的,例如:500 Internal Server Error,代表伺服器遇到了一個未曾預料的狀況,通常是伺服器的程式碼出錯

XMLHttpRequest 物件(object)

瀏覽器將它們的 HTTP API 定義在一個 XMLHttpRequest 物件之上。此物件的每個實體皆代表一組 request/response 的配對,而這個物件的「屬性」與「方法」讓你能夠:

  • 做出 POST、HEAD 及普通 GET 請求(request)的能力
  • 同步(sync)或非同步(async)擷取 Web 伺服器的回應(response),並且能夠以「文字」或一個「DOM」檔案……的形式返回內容
XMLHttpRequest,它並不限於只能用在 XML 檔案,它可以接收任何形式的文字檔案

建構子(constructor)

XMLHttpRequest()

呼叫任何方法前一定要先呼叫建構子,例如:

var request = XMLHttpRequest();

瀏覽器支援

XMLHttpRequest 物件第一次的出現是在 Microsoft 的 IE5 中,但在 IE5 及 IE6 中它是以 ActiveX 物件的形式提供。現在標準的 XMLHttpRequest() 建構子在 IE7 之前並沒有支援(現代瀏覽器都有支援),但可這樣模擬它的功能,以相容所有瀏覽器(包含 IE7 之前):

<script type="text/JavaScript">
// 建立 Ajax 物件
request = new ajaxRequest();

function ajaxRequest() {
    try {
        // (除 IE7 之前)支援所有現代瀏覽器
        var request = new XMLHttpRequest();
    } 
    catch (e1) {
        try {
            // (支援 IE6)如果有的話就用 ActiveX 物件的最新版本
            request = new ActiveXObject("Msxml2.XMLHTTP.6.0");
        } 
        catch (e2) {
            try {
                // (支援 IE5)否則就用較舊的版本
                request = new ActiveXObject("Msxml2.XMLHTTP.3.0");
            }
            catch (e3) {
                // 不支援 Ajax,拋出錯誤
                throw new Error("XMLHttpRequest is not supported");
            }
        }
    }
    return request;
}
</script>

屬性(attribute)

readyState

HTTP 請求的狀態,當一個 XMLHttpRequest 初次建立時,這個屬性的值為 0,直到接收到完整的 HTTP 回應(response),則值增加到 4。

readyState 值
狀態 描述
0 UNSENT open() 尚未被呼叫
1 OPEND open() 方法已被呼叫,但 send() 方法未被呼叫
2 HEADERS_RECEIVED send() 方法已被呼叫,而且可取得 header 與狀態
3 LOADING 回應資料下載中,此時 responseText 會擁有部分資料
4 DONE 回應(response)已經完成

responseText

回應請求的字串資料;如果請求失敗或尚未完成,則為 null

responseXML

回應請求的 DOM 文件物件(Document Object),如果請求失敗、尚未完成或回傳資料無法解析為 XML 或 HTML 檔,則為 null。回應會如同 text/html 一般一樣被解析。當 responseType 被設為 “document" 而且請求不是非同步,回應會如同 text/html 一般一樣被解析。

status

請求回應狀態,也就是 HTTP 狀態碼(status code),例如:200 代表成功;404 NOT Found,代表請求中引用的檔案不存在。

statusText

status 不一樣的是回應狀態字串(Status String),且會包含完整的回傳訊息,例如:"200 OK";"404 NOT Found"。

事件(event)

onreadystatechange

readyState 屬性每次改變時呼叫的 JavaScript 函式,是從使用者介面的執行緒中呼叫。

方法(method)

abort()

終止送出的請求。

這個方法會將 readyState 屬性重置為 0,回應不再必要的時候,可以呼叫這個方法。

getAllResponseHeaders()

以字串型態回傳所有回應表頭(response headers),如果沒有(readyState 小於 3)則回傳 null

請注意,對於多重請求,這個方法會回傳目前請求的回應而非最早的回應。

DOMString getAllResponseHeaders();

getResponseHeader()

以字串型態回傳指定表頭(header),如果沒有(readyState 小於 3)或請求尚未送出則回傳 null

DOMString getResponseHeader(DOMString header);
參數
header 回傳指定表頭,例如設定 "Content-Type" ,則返回 application/json; charset=UTF-8text/plain; charset=UTF-8…… 對應的資料類型

open()

初始化 HTTP 請求。

void open(
    DOMString method,
    DOMString url,
    optional boolean async,
    optional DOMString user,
    optional DOMString password
);
參數
method HTTP 方法,如 GET、POST、PUT 和 DELETE……
url 請求目的 URL
async (非必填)預設為 true,代表是否要送出非同步請求。如為 falsesend() 方法於收到回應前不會回傳;如為 true,透過事件處理器通知請求完成,當在多重請求下,此值必須為 true,否則會導致例外錯誤
user (非必填)預設為空字串,代表驗證用使用者名稱
password (非必填)預設為空字串,代表驗證用密碼

send()

發送 HTTP 請求(request),對於非同步(async)請求會立即回傳,同步(sync)請求則會等到請求完成才回傳。

void send();
void send(ArrayBuffer data);
void send(Blob data);
void send(Document data);
void send(DOMString? data);
void send(FormData data);

setRequestHeader()

設定 HTTP 請求表頭(header),必須要在呼叫 open()後,且在 send() 前呼叫。

使用 POST 方法必須設定 HTTP 請求表頭(header),GET 方法則不須設定
void setRequestHeader(
    DOMString header,
    DOMString value
);
參數
header 表頭名稱(header name),例如設定 "Content-Type"
value 表頭值(header value),例如設定 "application/x-www-form-urlencoded"

PHP Ajax 範例

以下將使用 JavaScript 與 jQuery 兩種方式搭配 PHP,來各別實作可查詢、新建員工的 Ajax 應用。

範例使用 JSON 作為「資料交換格式」,可參考 JSON 教學、格式

JavaScript 與 jQuery 所使用的 PHP 程式碼都相同:

伺服器端 – service.php
<?php
// 設置資料類型 json,編碼格式 utf-8
header('Content-Type: application/json; charset=UTF-8');

// 定義一個二維陣列來儲存員工資料,每筆員工資料為一個陣列
$staff = array(
            array('number' => '1020501', 'name' => "王一傑", 'sex' => '男'),
            array('number' => '1020502', 'name' => "王二傑", 'sex' => '男'),
            array('number' => '1020503', 'name' => "王三傑", 'sex' => '男'));

// 判斷如果是 GET 請求,則進行搜尋;如果是 POST 請求,則進行新建
// $_SERVER['REQUEST_METHOD'] 返回訪問頁面使用的請求方法
if ($_SERVER['REQUEST_METHOD'] == "GET") {
    search($staff);
} else if ($_SERVER['REQUEST_METHOD'] == "POST") {
    create();
}

// 通過員工編號搜尋
function search($staff) {
    // 檢查是否有員工編號的參數
    // isset() 方法檢測變數是否設置;empty() 方法判斷值是否為空
    // 超全域變數 $_GET 和 $_POST 用於收集表單資料
    if (!isset($_GET['number']) || empty($_GET['number'])) {
        echo json_encode(array('msg' => '沒有輸入員工編號!'));

        return;
    }

    // 搜尋員工編號
    for ($i = 0, $len = count($staff); $i < $len; $i++) {
        // 如果存在,儲存對應的陣列
        if ($staff[$i]['number'] == $_GET['number']) {
            $result = $staff[$i];
        }
    }

    // 將陣列編碼成 JSON 字串
    echo isset($result) ? json_encode($result) : json_encode(array('msg' => '沒有該員工!'));
}

// 新建員工
function create() {
    // 如果員工資料未填寫完全
    if (!isset($_POST['number']) || empty($_POST['number']) ||
        !isset($_POST['name']) || empty($_POST['name']) ||
        !isset($_POST['sex']) || empty($_POST['sex'])) {
        echo json_encode(array('msg' => '員工資料未填寫完全!'));

        return;
    }

    // 可將獲取的 POST 表單資料,儲存到資料庫(該部分未實作)

    // 儲存成功,返回員工姓名
    echo json_encode(array('name' => $_POST['name']));
}

JavaScript 實作範例

客戶端 – javascript-demo.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>JavaScript Ajax Demo</title>

<style type="text/css"> 
input, button, select {
    margin-bottom: 10px;
}
</style>
</head>
<body>

<h1>查詢員工</h1>
<label for="keyword">請輸入員工編號:</label>
<input type="text" id="keyword">

<button id="search">查詢</button>
<p id="searchResult"></p>

<h1>新建員工</h1>
<label for="staffNumber">請輸入員工編號:</label>
<input type="text" id="staffNumber"><br>

<label for="staffName">請輸入員工姓名:</label>
<input type="text" id="staffName"><br>

<label for="staffSex">請輸入員工性別:</label>
<select id="staffSex">
    <option value="男">男</option>
    <option value="女">女</option>
</select><br>

<button id="save">保存</button>
<p id="createResult"></p>

<script type="text/JavaScript">
document.getElementById("search").onclick = function() {
    // 發送 Ajax 查詢請求並處理
    var request = new XMLHttpRequest();
    request.open("GET", "service.php?number=" + document.getElementById("keyword").value);
    request.send();

    request.onreadystatechange = function() {
        // 伺服器請求完成
        if (request.readyState === 4) {
            // 伺服器回應成功
            if (request.status === 200) {
                var type = request.getResponseHeader("Content-Type");   // 取得回應類型

                // 判斷回應類型,這裡使用 JSON
                if (type.indexOf("application/json") === 0) {               
                    var data = JSON.parse(request.responseText);

                    if (data.number) {
                        document.getElementById("searchResult").innerHTML = '[找到員工] 員工編號:' +data.number + ', 姓名:' +
                                                                                data.name + ', 性別:' + data.sex;
                    } else {
                        document.getElementById("searchResult").innerHTML = data.msg;
                    }
                }
            } else {
                alert("發生錯誤: " + request.status);
            }
        }
    }
}

document.getElementById("save").onclick = function() {
    // 發送 Ajax 查詢請求並處理
    var request = new XMLHttpRequest();
    request.open("POST", "service.php");

    // POST 參數須使用 send() 發送
    var data = "name=" + document.getElementById("staffName").value +
                "&number=" + document.getElementById("staffNumber").value +
                "&sex=" + document.getElementById("staffSex").value;

    // POST 請求必須設置表頭在 open() 下面,send() 上面
    request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    request.send(data);

    request.onreadystatechange = function() {
        // 伺服器請求完成
        if (request.readyState === 4) {
            // 伺服器回應成功
            if (request.status === 200) {
                var type = request.getResponseHeader("Content-Type");   // 取得回應類型

                // 判斷回應類型,這裡使用 JSON
                if (type.indexOf("application/json") === 0) {               
                    var data = JSON.parse(request.responseText);

                    if (data.name) {
                        document.getElementById("createResult").innerHTML = '員工:' + data.name + ',儲存成功!';
                    } else {
                        document.getElementById("createResult").innerHTML = data.msg;
                    }
                }
            } else {
                alert("發生錯誤" + request.status);
            }
        }
    }
}
</script>

</body>
</html>

jQuery 實作範例

客戶端 – jquery-demo.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>jQuery Ajax Demo</title>

<style type="text/css"> 
input, button, select {
    margin-bottom: 10px;
}
</style>
</head>
<body>

<h1>查詢員工</h1>
<label for="keyword">請輸入員工編號:</label>
<input type="text" id="keyword">

<button id="search">查詢</button>
<p id="searchResult"></p>

<h1>新建員工</h1>
<label for="staffNumber">請輸入員工編號:</label>
<input type="text" id="staffNumber"><br>

<label for="staffName">請輸入員工姓名:</label>
<input type="text" id="staffName"><br>

<label for="staffSex">請輸入員工性別:</label>
<select id="staffSex">
    <option value="男">男</option>
    <option value="女">女</option>
</select><br>

<button id="save">保存</button>
<p id="createResult"></p>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script type="text/JavaScript">
$(document).ready(function() {
    $("#search").click(function() {
        $.ajax({
            type: "GET",
            url: "service.php?number= " + $("#keyword").val(),
            dataType: "json",
            success: function(data) {
                if (data.number) {
                    $("#searchResult").html(
                        '[找到員工] 員工編號:' +data.number + ', 姓名:' + data.name + ', 性別:' + data.sex
                    );
                } else {
                    $("#searchResult").html(data.msg);
                }
            },
            error: function(jqXHR) {
                alert("發生錯誤: " + jqXHR.status);
            }
        })
    })

    $("#save").click(function() {
        $.ajax({
            type: "POST",
            url: "service.php",
            dataType: "json",
            data: {
                name: $("#staffName").val(),
                number: $("#staffNumber").val(),
                sex: $("#staffSex").val()               
            },
            success: function(data) {
                if (data.name) {
                    $("#createResult").html('員工:' + data.name + ',儲存成功!');
                } else {
                    $("#createResult").html(data.msg);
                }                   
            },
            error: function(jqXHR) {
                alert("發生錯誤: " + jqXHR.status);
            }
        })
    })
});
</script>

</body>
</html>

7 則評論 to “Ajax JavaScript 與 jQuery 教學範例 for PHP”

  1. 學習中 說道:

    大大功德無量

    • SmallJacky 說道:

      謝謝!
      透過文章不僅能分享心得,而且在撰寫過程中還能提升一些基本觀念。

  2. cj 說道:

    難得看到 Client 端和 server 端都完整 po 出來的範例。
    謝謝,很實用

  3. 很棒 說道:

    想請問一下這ajax範例說明很清楚

    但我再跑搜尋到的員工編號 姓名跟性別會顯示NULL是為什麼呢?
    還有如果沒輸入東西或輸入錯誤的編號我的程式跑不出錯誤的MSG

    只有新增員工時會正常顯示輸出的東西
    再請指教一下我新手 感恩~

    • SmallJacky 說道:

      請問你是直接下載我的程式範例,執行所發生的問題嗎?

      您是用 JavaScript 或 jQuery?

      您所反應的問題應該是 php 程式的問題,且都是 GET 請求,建議可直接網址列執行 php 程式,並自行加上符合的 GET 參數,來 debug。

      如仍無法排除,請再告知。

      • 很棒 說道:

        沒錯是PHP GET程式問題,我在練習您指導的PHP時發現我多打了最下面的 ?> 然後執行get就跑不出東西,POST的部分也會出現NULL,請問一下為何多加?>會導致PHP無法正常回傳呢? 感謝您的指導!

        • SmallJacky 說道:

          我測試後,不管有無加入 ?> 均沒有發生您的問題,有可能是你 ?> 這附近有用到「全形空白」,造成 php 輸出一些警示訊息,導致 json 無法正確解析。

          原則上純 PHP 檔案必須省略 ?> 開關標籤,且檔案結尾必須空一行,至於為什麼可參考 http://cn2.php.net/manual/zh/language.basic-syntax.phptags.php。

          建議可直接網址列執行 php 程式,觀查是否輸出了非 json 的 php 警示訊息。

發表迴響