AJAX 入門:第一個範例


AJAX 入門:第一個範例

The following examples had been tested on Mozilla's Firefox and Microsoft's IE. The document is provided as is. You are welcomed to use it for non-commercial purpose.
Written by: 國立中興大學資管系呂瑞麟 Eric Jui-Lin Lu

請勿轉貼
看其他教材


在之前的 JavaScript 入門中,我們已經介紹了 XHTML 和 CSS 以及 DOM 的用法以及 應用範例,因此我們在第一個範例中,介紹 XMLHttpRequest 的用法以及它跟傳統 網頁寫法的不同處。在這個範例中,我們試圖從遠端取得台中地區的行政區域檔(該檔案是一個 XML 檔),然後將其內容顯示在網頁上。 傳統非 AJAX 的網頁,大多遵循以下的步驟:先撰寫一個含有 form 標籤的網頁; 以 HTML 原始碼下方的灰色按鈕為例,該按鈕的 HTML 原始碼如下所示:
<form method="POST" action="http://xml.nchu.edu.tw:8080/jlu/servlet/Taichung">
<input type="submit" value="取得台中行政區" />
</form>
然後按下灰色按鈕之後,該動作會向伺服器端的程式(範例中是一個名為 Taichung 的 Java servlet) 提出請求,然後在該程式執行結束後將結果回傳到一個新的網頁;請注意,新網頁 必須等待伺服器端的程式執行完成後才能顯示,而這一段時間原來的網頁也沒辦法 做任何的事情,只能默默的等待,這就是所謂的同步式(synchronous)的。 如果該伺服器端的程式因為執行的動作比較複雜或者網路反應比較慢,使用者 就必須多等一下,這也造成網頁的及時性變差。

在進一步解釋如何撰寫 AJAX 程式之前,讓我們先來體會一下所謂 AJAX 網頁的 非同步性與傳統同步性的網頁的差別。請按一下上方灰色的按鈕,取得的資料 會呈現在一個新的網頁上,所以會有一個明顯轉換的感覺;也請按一下下面的按鈕 (喔,那比較漂亮的按鈕不是 AJAX 的效果,而是 CSS 的效果,請不要誤會), 感覺上並沒有網頁的轉換,感覺上使用上比較像一般電腦桌面上的程式一樣。 你也可以試試看,在執行完兩個按鈕之後,按一下瀏覽器上的回上頁,她們的結果 也不一樣。建議多執行幾次去感受一下"同步性"與"非同步性"的即時性以及互動性 的差異。 (目前範例無法執行,因為 Google 無法上傳 XML 檔,請自行依據本文操作或者點選demo 網頁。)

取得台中行政區域

開發一個 AJAX 網頁大略可以分成下列幾個步驟:
  • 首先,先在網頁上放置必要的 HTML 元件;例如,在這個範例中,我們的 HTML 元件 如下:
    <div
        style="background-image: url(button.gif); background-repeat: no-repeat; 
        font-size: 14px; width: 132px; height: 28px; padding: 5px 0 0 5px; cursor: pointer;"
        onclick="makeRequest('taichung.xml')">
            取得台中行政區域
    </div>
    <div id="taichung"></div>
    
    其中,我們分成兩個 div 區塊:第一個 div 區塊被設計成一個互動的元件,除了 style 的設定之外,我們還設定了 onclick 的事件處理方式;如果使用者 click 的話, makeRequest() 方法會被執行。第二個區塊是為了能夠用來顯示從遠端取回來的 資料,,我們將其命名為 taichung,一開始 taichung 這個區塊是沒有內容的。
  • 在跟遠端伺服器要求檔案以前,我們需要產生 XMLHttpRequest 的物件。 跟所有撰寫 JavaScript 時會遇到的問題一樣,在產生 XMLHttpRequest 的物件, Mozilla 和 Internet Explorer 也有不同的語法,因此處理 AJAX 的 JavaScript 需要判斷你所使用的 browser 是哪一種。根據不同的瀏覽器, XMLHttpRequest 物件 的方式也不同,下列原始碼說明了各自的處理方式:
        if (window.XMLHttpRequest) { // 如果是 Mozilla, Safari,...
          http_request = new XMLHttpRequest();
        } else if (window.ActiveXObject) { // 如果是 IE
          try {
            // IE 又分成新版和舊版的,其處理方式也不同
            // 新版的 IE,目前 IE 9 OK
            http_request = new ActiveXObject("Msxml2.XMLHTTP");
          } catch (e) {
            try {
              // 舊版的 IE
              http_request = new ActiveXObject("Microsoft.XMLHTTP");
            } catch (e) {}
          }
        }
    
  • 有了 XMLHttpRequest 的物件之後,就是向遠端要求檔案,在這個範例中,我們 要取得的檔案名稱是 taichung.xml。
    http_request.open('GET', 'taichung.xml', true);
    http_request.send(null);
    
    1. open() 的第一個參數是 HTTP 的方法,看你的需要,以及 web server 支援的情形,你可以使用 GET、POST、或者 HEAD。建議使用大寫以符合 HTTP 的標準。
    2. open() 的第二個參數是你希望取得的資源的 URL。由於安全的機制, 你只能從同一個 web server 上取得資源;也就是說,若瀏覽器正在查看的網頁是 http://example.com/a.html,那麼 a.html 內的 AJAX 程式碼只能跟 http://example.com 提出 URL 的要求。以本網頁為例,由於本網頁 的伺服器是 web.nchu.edu.tw,那麼 taichung.xml 也必須在 web.nchu.edu.tw 上。 (嚴格的說,雖然這裡設定的 URL 只是 taichung.xml,實際上其完整的 URL 是 http://web.nchu.edu.tw/~jlu/classes/xml/ajax/taichung.xml)
    3. open() 的第三個參數是代表這個呼叫是不是非同步(即 AJAX 的 第一個 A)。因此,在大部分的情形下都是設為 true 代表在跟遠端取得 資源的同時,browser 可以繼續執行 Javascript 的內容。這個時候其實有兩件 事情同時在進行,一個是從遠端取得資料,另一個是繼續執行 JavaScript 的內容, 或者使用者可以繼續處理網頁上的資料,這也就是非同步的定義。
    4. 如果使用 GET 的方式,send() 的參數為 null。如果 使用 POST 的方式,我們除了要設定 MIME 的類型(如下)之外,
      http_request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
      
      我們還需要傳遞 HTML form 內的資料,例如,(這是基本的 HTTP GET/POST 的 URL 處理概念,需要解釋嗎?)
      http_request.send('name=value&anothername=othervalue&so=on');
      
  • 在進行下一個步驟說明之前,我想先說明"非同步的概念"。傳統的(或者同步的) 程式進行方式是:如果呼叫了 open() 以及 send() 之後, 若遠端程式還沒有執行結束並回傳資料,這個程式會一直停在那裏, 而無法繼續執行下一行指令;但是,如果是非同步的程式,在呼叫完 open() 以及 send() 之後,就算遠端還沒有執行完畢, 這個程式可以繼續執行下一行指令。這樣的優勢也造成了一些問題必須解決, 那就是"如果遠端程式執行完了,並將結果傳回來了",非同步執行中的程式 要如何知道這個情形?為了解決這樣的情形,AJAX 採用了事件處理機制。 一旦遠端程式執行結束,它會觸發一個已經完成了的事件,而我們撰寫的 AJAX 程式就可以事先定義一個處理"已經完成了"事件的處理器(在 Javascript 中,該處理器是一個函數)來處理這樣的情形。
  • 既然本範例是非同步的進行方式,在執行 open() 以及 send() 之前,我們必須先設定一些事件處理的函數來處理如"已經完成了"的事件。定義事件 處理函數的格式如下:
    http_request.onreadystatechange = function(){
        // do the thing
    };
    
  • 一般來說,事件處理函數首先要檢查的是目前 web server 對於我們之前 送出去的 http_request 的處理狀態為何。處理狀態的屬性名稱為 readyState, 它所有可能的值為
    • 0 (尚未初始化; 還沒呼叫 open())
    • 1 (載入中; http_request 已經設定好了,但是還沒呼叫 send())
    • 2 (載入完成; http_request 已經送給 server 了,server 已經開始 處理了,你可以取得 content header 了。)
    • 3 (可以互動的; http_request 已經部份完成了,所以有可能取得 部份處理完的資料)
    • 4 (完成的; compelte)
    在大部分的情形下,我們只對已經完成的狀態有興趣,所以我們的程式碼的格式如下:
    if (http_request.readyState == 4) {
        // everything is good, the response is received
    } else {
        // still not ready
    }
    
  • web server 的處理結束之後,會回傳一個處理結果;例如,我們的要求 完成了,而且所需要的資料也已經回傳了;但是也有可能是,我們的要求完成了, 但是處理結果卻是"找不到檔案"。因此,我們的程式還需要檢查處理 結果,處理結果是放在 status 這個屬性內,因此檢查處理結果 的程式碼如下:
    if (http_request.status == 200) {
        // 可以依照我們的需求來處理 xml 的資料了
    } else {
        // 404 代表檔案不存在,500 代表處理錯誤(Internal Server Error)
        // 自行決定要如何處理錯誤的情形
    }
    
  • 最後,就是接收來自 web server 的資料,http_request 提供兩種方式來 存取資料:
    • http_request.responseText 也就是 web server 回傳的資料是 一個字串物件,你需要利用字串的處理函數適當的處理該字串物件。
    • http_request.responseXML 也就是 web server 回傳的資料是一份 XMLDocuemnt 的物件,你可以利用 JavaScript 的 DOM APIs 來存取 這份 XML 物件。
  • 這個範例的完整原始碼:
    <script type="text/javascript" language="javascript">
      // 修改自 AJAX: Getting Started - MDC
      function makeRequest(url) {
        var http_request = false;
    
        if (window.XMLHttpRequest) { // Mozilla, Safari,...
          http_request = new XMLHttpRequest();
        } else if (window.ActiveXObject) { // IE
          try {
            http_request = new ActiveXObject("Msxml2.XMLHTTP");
          } catch (e) {
            try {
              http_request = new ActiveXObject("Microsoft.XMLHTTP");
            } catch (e) {}
          }
        }
    
        if (!http_request) {
          alert('Giving up :( Cannot create an XMLHTTP instance');
          return false;
        }
        // 定義事件處理函數為 alterContents()
        http_request.onreadystatechange = function() { 
                                          alertContents(http_request); };
        http_request.open('GET', url, true);
        http_request.send(null);
      }
    
      function alertContents(http_request) {
        if (http_request.readyState == 4) {
          if (http_request.status == 200) {
            var xmldoc = http_request.responseXML;
            var nodes = xmldoc.getElementsByTagName('area');
            var mesg = "";
            for(var i=0; i<nodes.length; i++) {
              mesg += nodes[i].firstChild.nodeValue + "\n";
            }
            //alert(mesg);
            document.getElementById("taichung").innerHTML = mesg;
          } else {
            alert('There was a problem with the request.');
          }
        }
      }
    </script>
    
  • 大家可以試著將 document.getElementById("taichung").innerHTML = mesg; 換成 alert(mesg);,然後試試看結果有什麼變化。
  • 參考資料:尤其是 DOM 以及 XMLHttpRequest 的使用方法。








Written by: 國立中興大學資管系呂瑞麟 Eric Jui-Lin Lu





3 則留言:

  1. 您好;
    我是一位AJAX的初學者
    今天我試圖將您的範例執行,但始終只有顯示"取得台中行政區域"的字樣,之後怎麼點擊這個字樣都沒有任何反應,我用alert("")測示的結果,確定可以跑到http_request.onreadystatechange = function() {alertContents(http_request); };
    這一行之前,另外,我有將taichung.xml載下來跟範例碼放在同個資料夾,請問您知道該如何解決這個問題嘛? 謝謝!!!

    回覆刪除
  2. 最主要的原因在於執行"取得台中行政區域"的主機,在 02/02/2013 因為學校整個停電檢修的情形下關機,需要到星期一才有人進機房開機。另一個原因就是屬於跨域呼叫,所有範例建議自行架個 Tomcat 的伺服器。

    回覆刪除
    回覆
    1. 謝謝您~~~~
      可是我已經把xml檔載下來了,那應該就跟伺服器有沒有開沒關係了吧?
      是因為xml跟html只單純放在電腦中的同一個資料夾無法產生連結的緣故嘛?
      接著
      我試著架設出一個伺服器再把xml檔跟HTML檔放在一起 看這樣可不可以讀到xml檔
      感謝您這麼迅速就回文!!!

      刪除