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

請勿轉貼


在動態網頁設計的時候,我們經常需要依據使用者所選取的資料來決定 呈現的選項;例如,在本範例中,使用者可以選擇某個特定的城市,然後 網頁會依照使用者所選擇的城市來顯示使用者可以選擇的行政區;所以,如果 使用者選擇台中市的話,它只能選擇台中市的行政區,而不可能選到高雄市 的苓雅區。 要解決這樣的問題,傳統上都使用 DHTML 或者配合後端程式的使用來達成。 如果僅僅使用 DHTML 的方式來做,使用者會從伺服器端下載所有的城市,以及每一個 城市的行政區,這會造成大量的資料傳輸,非常不好。可是如果使用 後端程式的方式,使用者就必須像第一個範例中非 AJAX 網頁的情形,必須 每一次重新載入新資料都要等待(同步化的原因),非常不方便。所以,我們 就試著以 AJAX 的方式來解決。(範例無法在 Google 執行,請自行操作或者點選demo 網頁。)
請選擇適當的城市以及行政區:

城市:
行政區:

說明:
  • 設計 HTML 元件,其內容如下:
    <form name="myform">
    城市:
    <select name="city"
            onChange="display(document.myform.city.options[selectedIndex].text);">
    <option value="none" selected="1">--</option>
    <option value="ks">高雄</option>
    <option value="chung">台中</option>
    </select><br/>
    <div id="label" class="off">行政區:</div>
    <select name="areas" class="off">
    </select>
    </form>
    
    範例中的 form 被命名為 myform,這是因為我們需要利用 CSS/Javascript 的機制 來顯示或者隱藏某些 form 內的元件;經由命名為 myform,Javascript 就可以 利用 DOM 的機制快速的找到 form 元件。 myform 裡面包含兩個下拉式選單,分別命名為 city 和 areas。city 的下拉式 選單包含可以選擇的城市,範例中只有高雄以及台中;讀者可以一邊閱讀,一邊 將你喜歡的城市也加進去。至於行政區的標籤(即 id 為 label 的 div 區塊)以及其 下拉式選單(name 為 areas)並不需要顯示,這時候我們使用 CSS 的 display: nonedisplay: inline 來處理;CSS 的設定如下:
    <style type="text/css">
      .off { display: none }
      .on  { display: inline }
    </style>
    
    在這個 CSS 中,我們設定了兩個 class selectors,分別為 on 和 off。由於 HTML 元件 label 和 areas 的 CSS 都設定為 off,當網頁載入的時候,使用者 並無法看到他們。 由於我們希望使用者選擇完了城市之後,AJAX 網頁能夠馬上依據使用者 選擇的城市,將該城市的行政區下載下來,並呈現在網頁上,我們必須為 city 下拉式選單設計一個事件處理器,該處理器需要在選單狀態改變的 時候執行,因此我們為 city 加上了 onChange 的屬性。如以上內容所示, 事件處理器命名為 display(),該處理器必須知道究竟是哪一個城市被選取, 而達成這個目標的 Javascript 物件為 document.myform.city.options[selectedIndex].text;其中,document.myform 會找到這個網頁中的 myform, 然後經由它找到 city;然後被選擇的城市可以經由 options[selectedIndex] 取得。取得之後,有兩種方式可以用來判斷,一種加上 .value,另一種是 加上 .text;例如,若使用者選擇台中,.value 回傳 "chung",而 .text 回傳"台中"。
  • 設計 display(str):由於 onChange 的時候,display() 會被呼叫,而且 城市的名稱會當作參數傳遞給 display()。所以,如果使用者選擇 "--",則 display() 只需要確保行政區的名稱(也就是 label)以及其下拉式選單 areas 不會出現,因此它們兩個必須如下的設定為 off:
        if(str == '--') {
          document.getElementById('label').className = "off";
          document.myform.areas.className = "off";
        }
    
    反之,如果使用者選擇其他的城市,我們就必須像之前一樣,初始化 XMLHttpRequest 物件,然後將 url 設定好;也就是說,如果使用者 選擇高雄,url 為 ks.xml;若選擇台中,url 則為 taichung.xml。 這一段程式碼跟之前的重複性高,我們就不再多解釋。
  • alertContents(http_request) 的設計:由於所有的行政區 XML 檔中,行政區 的名稱都是標籤名稱為 area 的文字節點(Text node),所以,程式中需要針對 這樣的結構把所有這樣的節點都找出來(也就是程式中 nodes 所代表);另外, 由於使用者可能選完台中之後,又選擇高雄,所以在把新的 nodes 的內容加到 current 節點(也就是代表下拉式選單 areas)之前,需要先把其內容刪除掉。
            var nodes = xmldoc.getElementsByTagName('area');
            var current = document.myform.areas;
    
            // 使用者可能選完台中之後,又選擇高雄
            // 如果有舊的 option,把它們清除掉
            var anode, tnode;
            while(current.hasChildNodes()) {
              anode = current.firstChild;
              current.removeChild(anode);
            }
    
    最後,程式就可以把新取得的行政區(也就是 nodes),利用 Javascript 的 DOM 方法(例如,document.createElement("option"))來產生必要的結果,其程式碼 如下:
    01        for(var i=0; i<nodes.length; i++) {
    02          anode = document.createElement("option");
    03          anode.setAttribute("value", i);
    04          if(i == 0)
    05            anode.setAttribute("selected", "1");
    06          current.appendChild(anode);
    07          tnode = document.createTextNode(nodes[i].firstChild.data);
    08          anode.appendChild(tnode);
    09        }
    
    在第 01 到 09 行的迴圈中,第 02 行先產生一個名為 option 的節點,這是因為 HTML 中下拉式選單(select)的子元素是 option;第 03 行為這個節點設定了 一個屬性名稱為 value,而屬性值為 i,而且(第 04-05 行中)如果這是第一個 option,則還為它加上一個 selected 的屬性;第 06 行將 option 這個節點成為 current 節點的子節點;第 07 行產生一個文字節點,內容為行政區的名稱; 第 08 行將這個文字節點變成 option 的子節點。 以台中為例,上述程式碼的第一個迴圈,所對應的節點變化如下表所示:
    行 數HTML 節點新節點
    第 02 行<select>
    </select>
    <option/>
    第 03 行<select>
    </select>
    <option value="0"/>
    第 04-05 行<select>
    </select>
    <option value="0" selected="1"/>
    第 06 行<select>
    <option value="0" selected="1"/>
    </select>
    加到 HTML 後就不在這裡顯示,避免變得太複雜。
    第 07 行<select>
    <option value="0" selected="1"/>
    </select>
    中區 (綠色代表文字節點)
    第 08 行<select>
    <option value="0" selected="1">中區</option>
    </select>
  • 完整的程式碼如下:
    <script language="javascript">
    <!--
      function display(str) {
        if(str == '--') {
          document.getElementById('label').className = "off";
          document.myform.areas.className = "off";
        } else {
          // insert AJAX code here
          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); };
          if(str == '高雄') {
            url = 'ks.xml';
          } else {
            url = 'taichung.xml';
          }
          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 current = document.myform.areas;
    
            var anode, tnode;
            while(current.hasChildNodes()) {
              anode = current.firstChild;
              current.removeChild(anode);
            }
    
            for(var i=0; i<nodes.length; i++) {
              anode = document.createElement("option");
              anode.setAttribute("value", i);
              if(i == 0) 
                anode.setAttribute("selected", "1");
              current.appendChild(anode);
              tnode = document.createTextNode(nodes[i].firstChild.data);
              anode.appendChild(tnode);
            }
    
            document.getElementById('label').className = "on";
            document.myform.areas.className = "on";
          } else {
            alert('There was a problem with the request.');
          }
        }
      }
    // -->
    </script>
    
  • 練習題: 請依據本範例,設計一對下拉式選單;第一個下拉式選單出現 產品分類(例如:3C 產品、時尚流行等),當選擇第一個選單項目之後,會出現 該產品分類的產品(例如,若使用者選擇 "3C 產品",則出現個人電腦、筆電、 平板電腦等產品)。









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





沒有留言:

張貼留言