AJAX JavaScript 與 jQuery 教學範例 for PHP

AJAX

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

簡介

什麼是 AJAX

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

表單(form)vs AJAX

表單 – 發送 HTTP 請求

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

AJAX – 發送 HTTP 請求

AJAX 應用可以僅向伺服器傳送並取回指定資料,接著在客戶端使用 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

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

POST

  • 適用於修改伺服器上的資料
  • 所有傳遞的參數(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」檔案... 的形式返回內容

建構子(constructor)

XMLHttpRequest()

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

var request = XMLHttpRequest();

瀏覽器支援

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

// 建立 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;
}

屬性(attribute)

readyState

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

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

send()

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

void open(
    DOMString method,
    DOMString url,
    optional boolean async,
    optional DOMString user,
    optional DOMString password
);

setRequestHeader()

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

void setRequestHeader(
    DOMString header,
    DOMString value
);
JavaScript XMLHttpRequest setRequestHeader() 參數
header 表頭名稱(header name),例如設定 Content-Type
value 表頭值(header value),例如設定 application/x-www-form-urlencoded

範例

程式

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

伺服器端 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>

下載

參考

在〈AJAX JavaScript 與 jQuery 教學範例 for PHP〉中有 20 則留言

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

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

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

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

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

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

      您是用 JavaScript 或 jQuery?

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

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

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

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

          原則上純 PHP 檔案必須省略 ?> 開關標籤,且檔案結尾必須空一行,至於為什麼可參考 PHP: PHP 标记 - Manual

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

  3. $.ajax({
    type: "POST",
    url: "service.php",
    dataType: "json",
    data: {
    name: $("#staffName").val(),
    number: $("#staffNumber").val(),
    sex: $("#staffSex").val()
    }
    請問如何將POST同時傳給service.php&service2.php

    • 只能傳送給一個 URL。

      我目前還未遇到需同時傳送兩個 URL,不知道是在什麼場合需要用到呢?

  4. 請問,假如我要做一個連動式的ajax該如何導出陣列資料呢?如:只輸入姓名,導出相關的電話、地址。

    html 部分
    function ImportAjax(username){
    $.ajax({
    type: 'POST',
    url: 'ajax.php',
    data: { user_name: username },
    success: function(opt){
    $('#user_name').html(opt);
    $('#user_tel').html(opt);
    $('#user_address').html(opt);
    }
    });
    }

    姓名:
    電話:
    地址:

    ajax.php 內容資料
    $sql = "SELECT * FROM " . TABLE . " WHERE user_name = '" . $_POST['user_name'] . "' ";
    $result = $db->query($sql);
    $row = $result->fetch_array();
    $result->free();
    echo $row['user_name'];
    echo $row['user_tel'];
    echo $row['user_address'];

    MySQL 資料庫裡面有三筆
    $a[1]['user_name']='大華'; $a[1]['user_tel']='02-12345'; $a[1]['user_address']='台北市***';
    $a[2]['user_name']='小明'; $a[2]['user_tel']='04-54321'; $a[2]['user_address']='台中市***';
    $a[3]['user_name']='小美'; $a[3]['user_tel']='06-33445'; $a[3]['user_address']='台南市***';

    • 1. 在輸入姓名的同時 AJAX(配合 JavaScript 的輸入事件)。
      2. 如資料庫有符合資料在輸出(建議都統一使用 JSON 格式,因為方便 JavaScript 解析)。
      3. 將對應的“電話”與“地址”塞到 html 標記。

      如有不了解在互相討論。

      • 感謝站長提示:
        我的作法是,如下:

        html 部分
        function ImportAjax(username){
        $.ajax({
        type: 'POST',
        dataType: 'json',
        url: 'ajax.php',
        data: { user_name: username },
        success: function(opt){
        $('#user_name').val(opt.user_name); //輸出姓名
        $('#user_tel').html(opt.user_tel); //輸出電話
        $('#user_address').val(opt.user_address); //輸出地址
        }
        });
        }

        ajax.php 內容資料
        $sql = "SELECT * FROM " . TABLE . " WHERE user_name = '" . $_POST['user_name'] . "' ";
        $result = $db->query($sql);
        $row = $result->fetch_array();
        $result->free();
        echo json_encode($row);

        已完成我的需求!謝謝~~

  5. 請問如果我要用laravel寫的話
    route要怎麼設定
    不知道怎麼把這動態的url塞到route裡面

發表留言