隨筆 - 312, 文章 - 14, 評論 - 1393, 引用 - 0
          數(shù)據(jù)加載中……

          AJAX從服務(wù)端獲取數(shù)據(jù)的三種方法

          本文為原創(chuàng),如需轉(zhuǎn)載,請注明作者和出處,謝謝!

              在本文中將給出一個例子來介紹使用AJAX技術(shù)從服務(wù)端獲得數(shù)據(jù)的三種方法。這個例子很簡單,就是兩個選擇框(html中的<select>標簽),通過選中第一個select的某一項后,會從服務(wù)端得到一些數(shù)據(jù),并加載到第2select中。

          方法一、從服務(wù)端獲得XML格式的數(shù)據(jù)

          從服務(wù)端獲得數(shù)據(jù)的最容易想到的方法就是在服務(wù)端反加一定格式的數(shù)據(jù),一般是XML格式,然后在服務(wù)端使用XMLDocument或其他技術(shù)來讀取這些數(shù)據(jù),并生成<select>標簽中選項的格式文本(<option>標簽)。下面的addOptions函數(shù)是這個例子的核心函數(shù),它負責根據(jù)從服務(wù)端獲得的數(shù)據(jù)生成<select>標簽中的<option>標簽。在這里所使用的方法是利用了<select>標簽的innerHTML屬性(僅限于firefox),如果是IE,要使用outerHTML屬性(IE<select>標簽的innerHTML屬性有一些小bug,讀者可以試著在IE中使用innerHTML屬性,看看會發(fā)生什么情況)。addOptions方法的實現(xiàn)代碼如下:

          // select表示<select>對象,xml表示XMLDocument對象
          function addOptions(select, xml)
          {    
              
          if(select)
              {
                  
          var options = "";
                  
          for(var i = 0; i < xml.childNodes[0].childNodes.length ; i++)
                  {  
                      
          if(xml.childNodes[0].childNodes[i].nodeName == "list")
                      {
                          
          var s = "";
                          
          if(isIE())               
                              s 
          = xml.childNodes[0].childNodes[i].text;         
                          
          else
                              s 
          = xml.childNodes[0].childNodes[i].textContent
                          options 
          += "<option value='" + s + "'>" ;
                          options 
          += s;
                          options 
          += "</option>"
                      }
                  }
                      
                  
          var id = select.id;
                  
          if(isIE())
                      select.outerHTML 
          = "<SELECT id='" + id + "' onchange='onChange(this)'>" + options + "</SELECT>";
                  
          else
                      select.innerHTML 
          = options;                
                  
              }
          }

              onReadState函數(shù)將在XMLHttpRequest對象的異步訪問服務(wù)端時調(diào)用。當readyState4時表示成功從服務(wù)端返回XML數(shù)據(jù)。這個函數(shù)的實現(xiàn)代碼如下:


          // myRequest表示XMLHttpRequest對象,selectId表示<select>標簽的id屬性值
          function onReadyState(myRequest, selectId) 

              
          if(myRequest.readyState == 4)   // 4表示成功獲得相應(yīng)信息
              {              
                  
          try
                  {
                      
          var xml = myRequest.responseXML;   // 獲得XMLDocument對象      
                      var kind = document.getElementById(selectId); // 獲得<select>對象
                      addOptions(kind, xml);  // 向<select>標簽中加入<option>標簽
                  }
                  
          catch(e)
                  {
                      alert(
          "onReadyState:" + e);
                  }
              }
          }

              getData函數(shù)負責向服務(wù)端發(fā)送請求,并設(shè)置異步事件。實現(xiàn)代碼如下:

          function getData(url, selectId)
          {
              
          var myRequest = getXMLHTTPRequest();  // 獲得一個XMLHttpRequest對象
              
              
          if(myRequest)
              {
                  myRequest.onreadystatechange 
          =  function() // 接收獲得數(shù)據(jù)狀態(tài)的事件函數(shù)
                  {                        
                      onReadyState(myRequest, selectId);  
                  }
                   
                  
          try 
                  {
                      myRequest.open( 
          "post", url, true);
                      
                  }
                  
          catch(e)
                  {
                      alert(e);
                  } 
                  
          try
                  {

                      myRequest.send(
          "");
            
                  }
                  
          catch(e)
                  {
                      alert(e);
                  }
            
              }
          }

             現(xiàn)在本例子的核心代碼已經(jīng)實現(xiàn)完成,下一步就是在html而加載時從服務(wù)端獲得第1<select>標簽的數(shù)據(jù),并將其加載到第1<select>標簽中。讓我們先看一下這個靜態(tài)的html代碼。

          <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
          <html>
              
          <head>
                  
          <title></title>
                  
          <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
                  
          <script type="text/javascript" src="myscript.js">
                  
          </script>
              
          </head>
              
          <body>
                  
          <select id="bigKind" onchange="onChange(this)" >
                       
                  
          </select>
                  
          <select id="smallKind" >
                     
                  
          </select>
              
          </body>
          </html>

           
              從上面代碼可以看出,這兩個<select>標簽分別是bigKindsmallKind,里面并沒有<option>標簽,這是因為<option>標簽要在javascript里動態(tài)加載。下面我們先來加載bigKind中的數(shù)據(jù)。

          window.onload = onLoad
          function onLoad()
          {                         

              
          try
              {
                  getData(
          "../GetXML""bigKind");
                         
              }
              
          catch(e)
              {
                  alert(
          "onLoad:" + e);
              }
          }


               其中GetXML是一個Servlet程序(讀者可以將其換成其他的服務(wù)端程序,如asp.netphp的)。下面是這個GetXML程序的實現(xiàn)代碼:

          package servlet;

          import java.io.*;
          import javax.servlet.*;
          import javax.servlet.http.*;
          import database.MyData;

          public class GetXML extends HttpServlet
          {

              
          protected void processRequest(HttpServletRequest request, HttpServletResponse response)
                      
          throws ServletException, IOException
              {
                  response.setContentType(
          "application/xml;charset=UTF-8");

                  PrintWriter out 
          = response.getWriter();

                  
          try
                  {
                      String s 
          = request.getParameter("kind");

                      out.println(
          "<data>");
                      
          if (s == null)
                      {
                          
          for (String key : MyData.data.keySet())
                          {
                              out.println(
          "<list>" + key + "</list>");
                          }
                      } 
          else
                      {
                          s 
          = java.net.URLDecoder.decode(s, "UTF-8");
                          System.out.println(s);
                          java.util.List
          <String> smallKind = MyData.data.get(s);

                          
          if (smallKind != null)
                          {
                              
          for (String kind : smallKind)
                              {

                                  out.println(
          "<list>" + kind + "</list>");
                              }
                          }
                      }
                      out.println(
          "</data>");

                  } 
          finally
                  {
                      out.close();
                  }
              }

              
          protected void doGet(HttpServletRequest request, HttpServletResponse response)
                      
          throws ServletException, IOException
              {
                  processRequest(request, response);
              }

              
          protected void doPost(HttpServletRequest request, HttpServletResponse response)
                      
          throws ServletException, IOException
              {
                  processRequest(request, response);
              }

              
          public String getServletInfo()
              {
                  
          return "Short description";
              }
          }

           

             不管讀者會不會javaservlet,從這個程序中的processRequest方法中都可以看出,首先會獲得請求參數(shù)kind,如果這個參數(shù)不存在,則返回bigKind所需要的數(shù)據(jù),以xml格式返回,類似于如下的格式:

          <data>
            
          <list>data1</list>
            
          <list>data2</list>
          </data>


              如果kind參數(shù)存在,則在MyData.data中查詢第2<select>標簽(smallKind)所需要的數(shù)據(jù)。data是一個Map類型。為了方便起見,本例子并未使用數(shù)據(jù)庫,而是在MyData類中定義了一個靜態(tài)的Map類型變量。MyData的實現(xiàn)代碼如下:

          package database;

          import java.util.*;

          public class MyData {

              
          public static Map<String, List<String>> data;    

              
          static {
                  
                  data 
          = new HashMap<String, List<String>>();
                  
                  List
          <String> eProducts = new LinkedList<String>();
                  eProducts.add(
          "手機");
                  eProducts.add(
          "數(shù)碼/IT");
                  eProducts.add(
          "家電");
                  eProducts.add(
          "電腦");
                          
                  data.put(
          "消費電子", eProducts);
                  
                  List
          <String> goods = new LinkedList<String>();
                  
                  goods.add(
          "化妝");
                  goods.add(
          "健康");
                  goods.add(
          "玩具");
                  goods.add(
          "辦公/文體 ");
                  goods.add(
          "童裝童鞋");
                  goods.add(
          "其他");
                  
                  data.put(
          "日用百貨", goods);
                  
                  List
          <String> books = new LinkedList<String>();
                  
                  books.add(
          "小說");
                  books.add(
          "動漫"); 
                  books.add(
          "經(jīng)濟");
                  books.add(
          "法律");
                  books.add(
          "計算機");
                  books.add(
          "英語");
                  books.add(
          "通訊");
                  books.add(
          "其他");
                  
                  data.put(
          "圖書", books)        ;                        
              }
          }


              其中data變量中的key值就是bigKind中的值,而每一個key對應(yīng)的值(一個List<String>對象就是smallKind中值的列表)。下面我們來實現(xiàn)當?shù)?/span>1<select>標簽bigKind變化時,更新smallKind標簽。<select>的onchange事件函數(shù)的代碼如下:

          function onChange(obj)
          {
              
          try
              {
                  getData(encodeURI(encodeURI(
          "../GetXML?kind=" +obj.options[obj.selectedIndex].value)), "smallKind");
               
              }
              
          catch(e)
              {
                  alert(e);
              }
          }

              這個函數(shù)是<select>標簽的onchange事件函數(shù)。obj表示<select>標簽本身。這個函數(shù)中只有一條有實際意義的語句,也就是調(diào)用了getData方法,這個方法人在onLoad方法中調(diào)用getData時差不多,只是在傳送url時使用了兩個encodeURI方法。由于XMLHttpRequest方法以utf-8向服務(wù)端發(fā)送數(shù)據(jù),因此,要使用兩個encodeURI向服務(wù)端發(fā)送%xx形式的utf-8編碼,然后在服務(wù)端進行解析。我們在GetXML中的processRequest方法中可以找到如下的一條語句:

          = java.net.URLDecoder.decode(s, "UTF-8");


              就是進行解碼操作。

              注:如果在IE中,客戶端可以不使用encodeURI對帶中文的URL進行編碼,服務(wù)端也不用解碼。在服務(wù)端仍然可以正常顯示中文。但在firefox中就必須要進行編碼和解碼。因此,要想跨瀏覽器,就需要使用本文所述的方法。

          方法二、直接獲得<option>...</option>內(nèi)容的字符串

              上面的獲得數(shù)據(jù)的方法是從服務(wù)端獲得了一個XML文檔,并轉(zhuǎn)換成XMLDocument對象,然后解析。這種方法雖然很好,但是操作XMLDocument對象還是有些麻煩,因此,我們可以在服務(wù)端直接反回<select>標簽所需要的<option>標簽字符串,然后將這些字符串傳給<select>對象的innerHTMLouterHTML就可以了。服務(wù)端的代碼和上面的實現(xiàn)代碼類似,只需要將<data>去掉,然后將<list>改為<option>后,并使用如下的語句來設(shè)置ContentType

          response.setContentType("text/html;charset=UTF-8");

          客戶端可通過XMLHttpRequest對象的responseText屬性獲得這些含有<option>的文本,并將其賦給innerHTMLouterHTML屬性。這種方法雖然很方便,但并不靈活。如果客戶端不使用<select>標簽,而是使用<table>或其他的標簽顯示數(shù)據(jù),那么返回的這些數(shù)據(jù)就沒什么用處了。而即方便,又靈活的應(yīng)該是下面要介紹的方法。

          方法三、從服務(wù)端返回javascript代碼,在客戶端使用eval函數(shù)執(zhí)行

              我們可以在服務(wù)端返回類似于如下的字符串:

              var options = new Array();

              options.push(‘data1’);

              options.push(‘data2’);

              然后使用eval函數(shù)執(zhí)行上面的字符串,這樣我們在javascript中就可以使用options數(shù)組了。我個人認為,使用數(shù)組要比使用XMLDocument更容易,代碼量也更少。如果要返回更為復(fù)雜的數(shù)據(jù),也可以使用javascript中的類或其他數(shù)據(jù)結(jié)構(gòu)。根據(jù)上面的思想,新的processRequest方法的代碼如下:

              protected void processRequest(HttpServletRequest request, HttpServletResponse response)
                      
          throws ServletException, IOException
              {
                  response.setContentType(
          "text/html;charset=UTF-8");

                  PrintWriter out 
          = response.getWriter();
                  out.println(
          "var options = new Array();");
                  
          try 
                  {
                      String s 
          = request.getParameter("kind");


                      
          if (s == null)
                      {                
                          
          for (String key : MyData.data.keySet())
                          {
                              out.println(
          "options.push('" + key + "');");
                          }
                      } 
          else
                      {
                          s 
          = java.net.URLDecoder.decode(s, "UTF-8");
                          System.out.println(s);
                          java.util.List
          <String> smallKind = MyData.data.get(s);

                          
          if (smallKind != null)
                          {
                              
          for (String kind : smallKind)
                              {
                                  out.println(
          "options.push('" + kind + "');");
                              }
                          }
                      }

                  } 
          finally
                  {
                      out.close();
                  }
              }

              客戶端經(jīng)過改進的addOptions函數(shù)如下:

          // javascript表示從服務(wù)端返回的javascript代碼字符串
          function addOptions(select, javascript)
          {    
              
          if(select)
              {   
                  
          if(select.id == "smallKind")
                  {
                      
          if(isIE())
                          select.options.length 
          = 0
                  }
                  
          var myOptions = "";
                  eval(javascript);  
          //執(zhí)行從服務(wù)端返回的javascript代碼
                  for(var i = 0; i < options.length ; i++)  // 從options數(shù)組中取數(shù)據(jù)
                  {             
                      
          var s = "";
                      
          if(isIE()) 
                      {
                          
                          select.options[select.options.length] 
          = new Option(options[i], options[i]);
                      }
                      
          else
                      {
                     
                          myOptions 
          += "<option value='" + options[i] + "'>" ;
                          myOptions 
          += options[i];
                          myOptions 
          += "</option>"
                      }
                  }
              }
                 
              
          var id = select.id;
              
          if(!isIE())    
                  select.innerHTML 
          =  myOptions;           
          }


              在上面的addOptions方法中還有一個不同是在IE中使用了<select>對象的options數(shù)組來添加選擇項,而不是使用outerHTML。這么做的好處是可以在onLoad方法中就獲得<select>的選項值。而如果使用outerHTMLhtml未裝載完時,<select>標簽中選擇項仍然為0。這樣在onLoad方法中就無法訪問<select>中的被加入項了,當然,在onchange事件中可以。 

              firefox中使用innerHTML時,在html未裝載完時,只要<select>標簽被裝載完(也就是調(diào)用了addOptions方法后),就可以訪問<select>標簽中的<option>了。個人感覺這一點要從IE做得好。順便說一句,筆者使用的是IE6,不知道ie7會是什么效果。如果哪位試過,可以跟貼。圖1是本例的效果圖。



                                         圖1

              本來想提供asp.net的例子來著,結(jié)果不知怎么著,vs2008asp.net設(shè)計視圖突然不響應(yīng)了,誰知道是怎么回事啊??





          Android開發(fā)完全講義(第2版)(本書版權(quán)已輸出到臺灣)

          http://product.dangdang.com/product.aspx?product_id=22741502



          Android高薪之路:Android程序員面試寶典 http://book.360buy.com/10970314.html


          新浪微博:http://t.sina.com.cn/androidguy   昵稱:李寧_Lining

          posted on 2008-05-25 23:16 銀河使者 閱讀(7798) 評論(18)  編輯  收藏 所屬分類: javaajaxjavascript 原創(chuàng)

          評論

          # re: AJAX從服務(wù)端獲取數(shù)據(jù)的三種方法  回復(fù)  更多評論   

          第一種方法通用性比較好,但比較復(fù)雜。
          第二種方法比較直接簡單。
          第三種方法比較垃圾。
          2008-05-26 10:28 | javajava

          # re: AJAX從服務(wù)端獲取數(shù)據(jù)的三種方法  回復(fù)  更多評論   

          不過我認為第3種方法比較好,可以使客戶端編程更容易,也更直觀。如果返回的xml比較復(fù)雜,使用第一種方法,javascript代碼會很多。

          但在實現(xiàn)項目中,這三種方法可以混合使用。
          2008-05-26 10:39 | 銀河使者

          # re: AJAX從服務(wù)端獲取數(shù)據(jù)的三種方法  回復(fù)  更多評論   

          innerHTML屬性在IE里有一點bug,我好像還沒碰到過。我用的是IE6.0
          2008-05-27 01:30 | 無花果

          # re: AJAX從服務(wù)端獲取數(shù)據(jù)的三種方法  回復(fù)  更多評論   

          并不是所有的html標簽的innerHTML都有bug。我碰到的是<select>標簽的innerHTML有一些bug。

          不信通過getElementById獲得<select>對象后,使用下面的代碼試試,看看會發(fā)生什么效果

          var select = getElementById("selectid");
          select.innerHTML = "<option>abc</option><option>ddd</option>";

          上面的代碼是出不來的選項的,不知怎么著,IE6把第一個<option>弄沒了。
          只有用下面的代碼才好使:


          var select = getElementById("selectid");
          select.outerHTML = "<select id='selectid' ><option>abc</option><option>ddd</option></select>";
          2008-05-27 08:49 | 銀河使者

          # re: AJAX從服務(wù)端獲取數(shù)據(jù)的三種方法  回復(fù)  更多評論   

          你試試重裝一下你的軟件看看能不能正常,樓主
          2008-05-27 17:58 | 懶人

          # re: AJAX從服務(wù)端獲取數(shù)據(jù)的三種方法  回復(fù)  更多評論   

          重裝軟件可是最后一招。 這最后一招殺手锏我可不想這么快就用。 還是研究一下vs2008的設(shè)計視圖為什么不好使吧。要是一不好使就重裝,那不是要把人弄瘋了。vs2008很大的,如果重裝還不行,是不是要重裝xp啊。^-^
          2008-05-27 21:58 | 銀河使者

          # re: AJAX從服務(wù)端獲取數(shù)據(jù)的三種方法  回復(fù)  更多評論   

          @銀河使者
          思路不錯,不過是不是安裝的時候哪個選項給漏掉了
          2008-05-29 11:04 | 懶人

          # re: AJAX從服務(wù)端獲取數(shù)據(jù)的三種方法  回復(fù)  更多評論   

          我一般安裝任何軟件都是完全安裝的,沒辦法,硬盤就是大。^-^,要說漏掉是不可能的,我都是全選的。單位的機器也是按著同樣的方法安的,沒有問題,就是家里的dell筆記本出這種事。昨天安了個vs2008的patch,也不好使。
          2008-05-29 12:39 | 銀河使者

          # re: AJAX從服務(wù)端獲取數(shù)據(jù)的三種方法  回復(fù)  更多評論   

          ajax不就是后臺發(fā)送請求,接收返回的結(jié)果,然后解析,不是嗎?
          2008-05-29 18:15 | stanley_xu

          # re: AJAX從服務(wù)端獲取數(shù)據(jù)的三種方法  回復(fù)  更多評論   

          ajax從原理上講很簡單。其實任何技術(shù)從基本原理上看都很簡單。但實現(xiàn)起來,確是另外一回事。
          2008-05-29 18:45 | 銀河使者

          # re: AJAX從服務(wù)端獲取數(shù)據(jù)的三種方法  回復(fù)  更多評論   

          LZ說的是,實現(xiàn)起來的感覺.......
          現(xiàn)在的AJAX框架技術(shù)太多了,搞得我不知用哪種好,唉!
          2008-05-30 16:00 | hulu

          # re: AJAX從服務(wù)端獲取數(shù)據(jù)的三種方法  回復(fù)  更多評論   

          只要適合自己的,就是好的。
          2008-05-30 20:45 | 銀河使者

          # re: AJAX從服務(wù)端獲取數(shù)據(jù)的三種方法[未登錄]  回復(fù)  更多評論   

          下次能不能寫全了再給放上來,文章代碼內(nèi)容不全,讓我調(diào)試了半天才搞定。
          2008-07-19 19:39 | eric

          # re: AJAX從服務(wù)端獲取數(shù)據(jù)的三種方法  回復(fù)  更多評論   

          本文的只是代碼片段,為了演示這個功能,本文已經(jīng)假設(shè)讀者對javascript比較熟悉,因此,并沒有列出所有的代碼。如果那么,會無法突出重點,讓想看這部分內(nèi)容的讀者不容易找到了。在以后的文章,我會提供源代碼供讀者下載學(xué)習(xí)。謝謝關(guān)注本文。
          2008-07-19 19:51 | 銀河使者

          # re: AJAX從服務(wù)端獲取數(shù)據(jù)的三種方法  回復(fù)  更多評論   

          JSON更爽.
          2008-07-28 18:37 | Beniao

          # re: AJAX從服務(wù)端獲取數(shù)據(jù)的三種方法  回復(fù)  更多評論   

          json里面這些都自動處理了,請看我的這篇文章
          http://www.aygfsteel.com/nokiaguy/archive/2008/07/04/212515.html
          2008-07-28 20:34 | 銀河使者

          # re: AJAX從服務(wù)端獲取數(shù)據(jù)的三種方法  回復(fù)  更多評論   

          i8里面第二個select選擇的時候就沒有了。不知道是怎么回事。我用的outerHTML
          2009-03-24 22:50 | liuhua

          # re: AJAX從服務(wù)端獲取數(shù)據(jù)的三種方法  回復(fù)  更多評論   

          @liuhua
          我在ie6下測的,it7和ie8沒測過,也許是不兼容的問題吧。哎~
          2009-03-25 08:27 | 銀河使者
          主站蜘蛛池模板: 乌海市| 永年县| 偏关县| 晋江市| 内乡县| 普洱| 彰化市| 荆州市| 科技| 廉江市| 南溪县| 黄大仙区| 大安市| 都兰县| 永新县| 奎屯市| 宜春市| 文成县| 桂阳县| 缙云县| 顺昌县| 庆城县| 报价| 连云港市| 永顺县| 中山市| 正安县| 基隆市| 黄陵县| 繁峙县| 商南县| 赫章县| 桂林市| 阳新县| 伊春市| 普兰县| 邹城市| 海阳市| 民勤县| 大邑县| 灵寿县|