AJAX JavaScript 與 jQuery 教學範例 for PHP
使用 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 運作原理
- 使用者在「瀏覽器」觸發一個事件,例如點擊按鈕
- 將上述獲的事件的同時,使用 JavaScript 的
XMLHttpRequest
物件,在背景對「Web 伺服器」發送一個 HTTP 請求,達到與「Web 伺服器」進行資料的非同步交換 - 將從「Web 伺服器」取得的資料,使用 JavaScript 操作 DOM,來實現動態局部更新「瀏覽器」的網頁內容
HTTP
完整的 HTTP 流程
一個完整的 HTTP 流程,通常有下面七個步驟:
- 建立 TCP 連接
- 瀏覽器:向「Web 伺服器」發送請求命令
- 瀏覽器:發送請求表頭(headers)
- Web 伺服器:回應(response)
- Web 伺服器:發送回應(response)資訊
- Web 伺服器:向「瀏覽器」發送資料
- Web 伺服器:關閉 TCP 連接
HTTP 請求方法(GET 與 POST)
GET
- 適用於資訊獲取(查詢結果)
- 使用 URL 傳遞參數(name=value)
- 對所發送資訊的數量有限制,一般在 2,000 個字串
POST
- 適用於修改伺服器上的資料
- 所有傳遞的參數(name=value)將被嵌入 HTTP 的請求主體(body)
- 對所發送資訊的數量無限制
HTTP 請求(request)
一個 HTTP 請求由四個部分構成:
- HTTP 請求方法(method)或「動詞」(verb),比如是 GET 或 POST 請求
- 要請求的 URL,就是請求的網址
- 一組選擇性的請求表頭(headers),包含一些「客戶端環境」與「身份驗證」資訊...
- 一個選擇性的請求主體(body),也就是請求正文,可包含客戶提交的「查詢字串」或「表單」資訊...
HTTP 請求內容
- 請求方法:GET
- 請求地址:/service.php?number=1020501
- HTTP 版本:HTTP/1.1
- 請求表頭:紅框部分
- 請求主體:綠框部分
GET /service.php?number=1020501 HTTP/1.1Host: 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.4number=1020501
HTTP 回應(response)
伺服器的 HTTP 回應(response)有三個部分:
- 由數值與文字所組成的狀態碼(status code),指示請求成功或失敗
- 一組回應表頭(headers)包含許多有用的資訊,例如伺服器類型、日期時間、內容類型和長度...
- 回應主體(body)也就是回應正文,例如「文字字串」或「HTML 代碼」...
HTTP 回應內容
- HTTP 版本:HTTP/1.1
- 回應狀態碼:200 OK,代表請求成功
- 回應資料類型:application/json; charset=UTF-8,代表資料類型 JSON;編碼格式 UTF-8
- 回應表頭:紅框部分
- 回應主體:綠框部分(這裡回應的資料格式為 JSON)
HTTP/1.1 200 OKDate: 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 之前):
// 建立 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。
值 | 狀態 | 描述 |
---|---|---|
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-8、text/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 ,代表是否要送出非同步請求。如為 false ,send() 方法於收到回應前不會回傳;如為
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
);
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>
下載
參考
本著作係採用創用 CC 姓名標示-相同方式分享 3.0 台灣 授權條款授權.
大大功德無量
謝謝!
透過文章不僅能分享心得,而且在撰寫過程中還能提升一些基本觀念。
難得看到 Client 端和 server 端都完整 po 出來的範例。
謝謝,很實用
想請問一下這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 警示訊息。
$.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,不知道是在什麼場合需要用到呢?
請問,假如我要做一個連動式的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);
已完成我的需求!謝謝~~
真是一篇優質的技術文件,大推。
請問如果我要用laravel寫的話
route要怎麼設定
不知道怎麼把這動態的url塞到route裡面
我尚未使用過 Laraval,抱歉。
有基礎又有實做的好文,讀完淚流滿面QQ
很高興對您有幫助
這篇好棒
有幫到您就好~