JavaScript AJAX (Asynchronous JavaScript and XML)

AJAX 的全名是 Asynchronous JavaScript and XML。

一般你瀏覽一個新的網頁,你的瀏覽器 (browser) 要先等遠端伺服器 (web server) 傳回網頁資料後,才載入這一整個新的頁面,在這等待的期間你沒法做其他事情只能等,這種行為我們稱做同步 (synchronous)。

那 AJAX 是什麼樣子的技術,AJAX 的特性是什麼?

  • 無須重新載入整個頁面,便能對遠端伺服器發送請求 (請求會在瀏覽器背景執行)。
  • 讓你可以只更新網頁中的一小部分內容,也因為 server 返回的資料量更小,讓網頁反應速度更快。
  • 在跟 server 請求新內容同時,你可以在頁面中繼續做其他操作,不用停住等待,這種行為我們稱做非同步 (asynchronous)。

AJAX 名稱中有一個 XML,本意是指從 server 端返回的資料格式,這讓初學者特別容易誤會的是,那 AJAX 是跟 XML 有直接關聯嗎?還是一定只能用 XML 格式?答案是沒有直接關係,反而現在實務上最常用的 AJAX 資料交換格式是 JSON

AJAX 只是一種網頁程式設計概念,它是由幾個既有的技術所組成:

  • 利用 JavaScript 的 XMLHttpRequest 物件 (object) 與遠端的 web server 進行非同步的資料交換。
  • 利用 JavaScript / DOM / HTML / CSS 來動態改變頁面上的內容。

AJAX 的運作流程

AJAX 的運作概念大概是這樣子的:

AJAX

圖片來自於 Wikipedia AJAX

  1. 當頁面上某個事件 (event) 發生時,例如使用者點了某個按鈕。
  2. 建立一個 JavaScript XMLHttpRequest object。
  3. 透過 XMLHttpRequest 物件來向遠端 web server 發送 HTTP 請求 (request)。
  4. web server 返回請求結果。
  5. 在 client 端用 JavaScript 讀取返回的結果 (如果判斷是 JSON 資料格式,則用 JSON.parse 解開內容)。
  6. 根據返回的資料,做任何後續動作 (或不動作),例如更新網頁上的內容。

XMLHttpRequest Object

XMLHttpRequest 物件用來建立 HTTP 請求,語法:

var httpRequest = new XMLHttpRequest();

IE 在 IE7 開始才支援 XMLHttpRequest Object,如果是 IE6 以下則要用 IE 專屬的 ActiveXObject:

var httpRequest = new ActiveXObject('Microsoft.XMLHTTP');

XMLHttpRequest 物件建立後,接下來就用其 onreadystatechange 方法來綁定等 server 資料返回時要執行的 callback 函數:

httpRequest.onreadystatechange = function() {
    // ...
};

XMLHttpRequest.open(method, url, async)

接下來用 open 方法指定要請求的網址 (URL),語法:

httpRequest.open(method, url, async);
  • 參數 method 指定 HTTP method 像是 "GET", "POST", "PUT", "DELETE"
  • 參數 url 指定要發送請求的網址
  • 參數 async 是一個布林值 (boolean) 表示是否要做非同步請求,預設是 true 非同步

例子:

httpRequest.open('GET', '/api/test.php?param=foobar');

XMLHttpRequest.send(null | formData)

最後用 send 方法發送出遠端 HTTP 請求:

httpRequest.send(null);

如果你是想用 HTTP POST,send 方法的第一個參數可以用來傳值,值的格式是 Form data 格式:

httpRequest.open('POST', '/api/test.php');

httpRequest.send('name=value&anothername=' + encodeURIComponent(myVar) + '&so=on');

XMLHttpRequest.setRequestHeader(header, value)

此外,當你使用 HTTP POST 資料的時候,需要設定 HTTP 的 Content-Type 標頭 (header) 為 application/x-www-form-urlencoded,你可以用 setRequestHeader 方法來設定 HTTP header:

httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

XMLHttpRequest.readyState, XMLHttpRequest.statusText, XMLHttpRequest.responseText

XMLHttpRequest 物件還有幾個屬性 (property) 你需要知道的:

屬性說明
readyState目前 XMLHttpRequest 物件的狀態
0 (uninitialized)
1 (loading)
2 (loaded)
3 (interactive)
4 (complete) 請求完成
status返回的 HTTP 狀態 (status)。像是:
200: "OK"
403: "Forbidden"
404: "Page not found"
responseTextserver 返回的資料,是一個字串

XMLHttpRequest.onreadystatechange

而每當 XMLHttpRequest 物件狀態改變時,透過 onreadystatechange 綁定的函數就會被執行,接著我們來看怎麼處理 server 返回的資料 (HTTP response):

httpRequest.onreadystatechange = function() {

    // 等狀態變成請求完成狀態
    if (httpRequest.readyState === 4) {
    
        // 只處理 server 返回正常的 HTTP 200 狀態
        if (httpRequest.status == 200) {
        
            // 顯示 server 返回的內容
            alert(httpRequest.responseText);
            
            // 或,如果返回的資料格式是 JSON
            var response = JSON.parse(httpRequest.responseText);
            
            // ...
        }
    }
};

XMLHttpRequest.getResponseHeader(name)

另外,getResponseHeader 方法可以用來取得 HTTP response 的 header:

httpRequest.onreadystatechange = function() {

    if (httpRequest.readyState === 4) {    
        if (httpRequest.status == 200) {
        
            // 顯示 Last-Modified response header
            alert(httpRequest.getResponseHeader('Last-Modified'));
            
            // 顯示 Content-Type response header
            httpRequest.getResponseHeader('Content-Type');
            
            // ...
        }
    }
};

Async = false

前面提到 open 方法的第三個參數是 false 時,表示使用同步模式 (synchronous),一般很少使用,如果你指定同步模式,在發送 HTTP request 直到 server 返回資料這期間,JavaScript 會停止執行網頁也會暫停住不能操作。

而 Async = false 時,也不是用 onreadystatechange callback,而是在 send 後面直接取得 response data (因為執行 send 之後,JavaScript 會停止執行一直等到 reponse 回來為止):

httpRequest.open('GET', '/api/test', false);
httpRequest.send();
alert(httpRequest.responseText);

AJAX 完整範例 (Example)

// 建立 XMLHttpRequest 物件
var httpRequest;

if (window.XMLHttpRequest) {
    httpRequest = new XMLHttpRequest();

} else {
    // 跨瀏覽器相容 IE6 以下
    httpRequest = new ActiveXObject('Microsoft.XMLHTTP');
}

// AJAX callback
httpRequest.onreadystatechange = function() {

    // 等狀態變成請求完成狀態
    if (httpRequest.readyState === 4) {
    
        // 只處理 server 返回正常的 HTTP 200 狀態
        if (httpRequest.status == 200) {
            
            // 解開 server 返回的 JSON 資料格式
            var jsonResponse = JSON.parse(httpRequest.responseText);
            
            // 更新頁面內容
            document.getElementById('user').innerHTML = jsonResponse.userName;
            
        } else {
            alert('ERROR - server status code: ' + httpRequest.status);
        }
    }
};

// 使用 HTTP GET 方法,從 URL /api/get_something 請求資料
httpRequest.open('GET', '/api/get_something');

// 送出 HTTP 請求
httpRequest.send(null);