Ajax無刷新方法縱覽(1)—XMLHttpRequest對象

一.XMLHttpRequest對象

1.XHR對象的調用及其屬性和方法

關於無刷新技術,很自然的會想到XMLHttpRequest(XHR)對象,這是Ajax中請求服務器局部刷新的技術基礎,在主流的瀏覽中都支持XHR,不過是在不同版本和類型的瀏覽器是用不同的方法獲取這個對象。 

var xhr;
try
{xhr= new ActiveXObject('Msxml2.XMLHTTP'); /*IE5.0*/}
catch(e)
{
 try{xhr= new ActiveXObject('Microsoft.XMLHTTP'); /*IE5.5*/}
 catch(e)
 {
  try{xhr= new XMLHttpRequest();/*IE7及其他類型瀏覽器*/}
  catch(e){}
 }
}

上面利用異常的機制實現了瀏覽器的兼容。

XHR有同步和異步兩種方式,同步請求時,頁面會等待服務器的響應結束才允許用戶繼續進行其他工作,因此如果這個請求阻塞用戶很久,那是很令人失望的。所以,通常使用的是異步請求,讓用戶可以在等待響應的期間可以繼續做其他的。下面是典型的打開一個異步請求並處理的代碼。 

xhr.open("get","ajax.aspx?arg=a&arg=b"); //創建請求
xhr.onreadystatechange
=
function() { //註冊請求狀態改變的處理事件
    
if(xhr.readyState==4
) {
        
if(xhr.status==200
) {
            var text
=
xhr.responseText;
        }      
    }                
}
xhr.send(null);//發送請求

 —open()方法是向服務器發送一個請求的前的準備,使用它可以創建一個請求。其完整的形式是open(method,url,asynchronous,username,password),正如上面的示例,後面三個參數是可選的。第三個參數表示請求是否是異步,不設值時,默認的便是true即異步。第一個參數是發送請求的方式,一般是GET或POST比較常用(其他的還有PUT、DELETE等方法)。

  • GET方法如果傳遞參數,是用緊跟在"?"後面用"&"分隔開的名值對,作爲查詢字符串提交。在服務器端處理時一般用request.querystring提取(PHP用$_GET("參數名"))。由於send方法中參數body是必須的,而GET方法下不發送數據到服務器端,所以可以爲null。
  • POST方法,實際也是可以在URL中傳遞查詢字符串的,並在服務器端用同樣的方法提取。然而使用POST方法,是因爲需要發送影響服務器狀態或更大量的數據。它發送的數據是send方法中body參數的值,該參數也是查詢字符串的形式(這也許比較常見,或者說更容易用標準的方式可以提取,就如同從表單用POST方式提交,而實際上如果不設置請求的頭部信息,它可以是任何形式)。在服務器端獲取時,一般使用request.form提取(PHP用$_POST("參數名"))需要特別注意的是,由於是CGI風格(表單式的提交)上傳瀏覽器數據,所以需要用setRequestHeader方法設置請求的頭部信息,這必須要在send方法前設置。 
            
    xhr.open("post","ajax.aspx")
    xhr.setRequestHeader(
    "Content-Type","application/x-www-form-urlencoded"
    );
    xhr.onreadystatechange
    =
    function() {
        
    if(xmlhttp.readyState==4
    ) {
            
    if(xmlhttp.status==200
    ) {
                var text
    =
    xhr.responseText;
            }      
        }                
    }
    xhr.send(
    "arg=a&arg=b");

—send()方法發送請求。可以發現爲XHR對象上註冊了一個onreadystatechange事件處理函數。它使得在send方法發送請求後,請求的狀態一但發生改變就會觸發這個事件。爲了使事件發生時可以被接受處理,因此應該將該事件的處理函數的聲明放在send方法的前面。實際上open和send方法都會觸發onreadystatechange,然而我們比較關心發送請求後的狀態改變因此只在send方法前面聲明瞭處理函數就應該足夠了。
        (題外話:這似乎讓有點不清楚,爲什麼要在send前面。如果可以明白Javascript是面向對象的語言,那麼就會更清楚,這無非是在給成員賦值。用script標籤中定義的方法和成員來說明,通常我們可以在JS代碼的任何地方直接使用全局的方法或變量。而實際上,任何的在script標籤中定義的全局方法或變量都是window類的成員,當瀏覽器生成window對象後,也可以用"window.方法名或變量名"的方法訪問。在頁面加載的過程中解釋這些全局的成員,實際上是在解析這個window類,不存在定義先後的問題,因爲它們在生成window對象後無論定義先後都會成爲的對象成員,每一個類的全局成員的調用都是實際上通過"對象.方法名"的方法訪問到的,而在上面的XHR對象最開始並不是我們定義的,我們使用第一段代碼獲得的,它自己定義瞭如open、send、onreadystatechangereadyState、status這些成員。我們爲它定義的onreadystatechange事件處理函數什麼時候開始存在,取決於我們在"new XHR對象"聲名引用後,什麼時候定義的onreadystatechange事件處理的函數。這實際是在給函數的成員賦值,不過是爲事件處理函數成員賦了一個匿名函數)

—readyState變量(也可以稱爲屬性)是用於記錄下請求的狀態。

  • 當XHR對象產生時它的狀態爲0(對象已經建立但還未初始化)。
  • open方法執行後狀態爲1(open已被調用)。
  • send方法執行後狀態爲2(send已被調用)。
  • 請求發送成功狀態爲3(正在接受數據)。
  • 請求完成時狀態爲4(請求完成,返回數據已被接收)。

狀態1到3在不同的瀏覽器中的解釋不同,但是我們通常比較關心狀態4即請求完成。如果onreadystatechange處理中readyState的值爲4就可以繼續作請求完成的處理了。

—status屬性用於對請求結果的判斷。status獲得服務器返回的HTTP響應狀態編碼,表示服務器處理請求結果的狀態。這些編碼和瀏覽器正常打開一個頁面的響應狀態編碼一樣,包括的404(請求資源未找到)和500(內部服務錯誤)等,而200(請求成功)在一般的瀏覽頁面響應中就是把頁面顯示出來不會有其他的,而這裏我們需要判斷是否響應狀態是200,如果是就操作響應返回的結果。

—statusText屬性獲取響應編碼的描述,如" Not Found"等。

—responseText屬性獲取響應返回的文本,它可以是純文本或HTML片段。它是接受請求的頁面處理完成後,輸出到請求頁面上的結果內容,這和直接用open方法中的URL參數地址訪問請求頁面是得到同一結果。

—responseXML屬性返回XML文檔(DOM)對象,只有當服務器端返回一個XML格式的文檔纔有效,所以以需要爲響應設置頭部信息。如果請求的是一個.xml後綴的文件,服務器會自動的設置。否則,需要用代碼設置,如response.ContentType="text/xml"。因爲獲取的是一個DOM模型對象,所以可以用DOM模型進行解析和操作。

—abort()方法取消一個請求,並把請求狀態重置爲0。

—getAllResponseHeaders()方法,獲取包含所有響應頭部信息的一個字符串,每個信息之間用換行符號分隔。

—getResponseHeader()方法,獲取指定的響應頭部信息,接受一個參數,是頭部信息字段名稱的字符串。

—setRequestHeader()方法,指定請求的頭部信息。

*其他的屬性和方法

  • —responseBody屬性(IE6及以下 Olny)獲取服務器返回的未經編碼的二進制數據,是數組格式。在IE7中已經無法獲取該屬性。
  • —responseStream屬性(IE6及以下 Olny)返回AdodbStream對象,是原始編碼的數據流,它的編碼形式取決於服務器發送的編碼形式,可以是二進制、UTF8等。它還可以讀取非文本數據,如圖象、應用程序等。這個屬性只有在IE才被支持,因爲它獲取的AdodbStream對象是IE中特有的,在IE中是ActiveX對象註冊的,在IE7中AdodbStream已經被禁止,IE6及以下可以直接創建。在其他瀏覽器下會出錯。這個AdodbStream可寫,並且可以寫入文件到客戶的主機上,因此有很大安全隱患,許多的木馬開始用AdodbStream對象的方法和屬性通過瀏覽器傳播木馬(FireFox等瀏覽器沒有這類型ActiveX控件,因此很安全)。其實,可以把responseBody中的二進制數據寫入到AdodbStream對象中,與直接從responseStream屬性獲取對象是一樣的。如下面的示例:
      xhr.open(("get","http://website.com/1.exe",true); 
      xhr.send(
    null
    ); 
      var adoStream
    =new ActiveXObject("ADODB.Stream"
    ); 
      adoStream.Type
    =1;//數據流類型,1表示二進制 

      adoStream.Open(); //打開對象
      adoStream.write(xhr.responseBody); //寫入數據
      adoStream.SaveToFile("1.exe",2); //第一參數是保存文件的地址;第二參數是保存的方式,1-如果文件不存在就創建,2-如果文件已存在,用數據流覆蓋文件中的數據
     adoStream.Close();//關閉對象 
  •  —overrideMimeType()方法(FireFox Only) 是對響應的頁面內容以特定編碼進行識別。目前,大多數的網頁都是UTF8編碼。但是,仍然有不少中文網頁文件保存使用了GB2312編碼,而XHR對像默認的是以UTF8編碼識別網頁,因此有時訪問會產生亂碼,FireFox瀏覽器提供了overrideMimeType方法解決這個問題。
    xhr.open("GET""ajax.aspx");  
    xhr.overrideMimeType(
    "text/html;charset=gb2312");//以gb2312編碼識別數據  

    xhr.onreadystatechange = function(){  
    if (xhr.readyState == 4
    )  
        
    if (xhr.status == 200
    )  
            var text
    =
    xhr.responseText; 
    };  
    xhr.send(
    null);  
    遺憾的是IE下並沒有這個方法。但是,IE也有自己的解決方法,其中主要的是使用到了IE特有的execScript()瀏覽器方法。該方法接受兩個字符串參數:第一個參數是腳本的代碼字符串,第二是代碼的腳本語言類型。主要的代碼如下:
    function gb2_To_utf8(data){  
        var glbEncode 
    =
     [];  
        gb2utf8_data 
    =
     data;  
        execScript(
    "gb2utf8_data = MidB(gb2utf8_data, 1)""VBScript"); //IE Olny,VBScript是隻有IE支持的腳本語言

        var t=escape(gb2utf8_data).replace(/%u/g,"").replace(/(.{2})(.{2})/g,"%$2%$1").replace(/%([A-Z].)%(.{2})/g,"@$1$2");  
        t
    =t.split("@"
    );  
        var i
    =0,j=
    t.length,k;  
        
    while(++i<
    j) {  
            k
    =t[i].substring(0,4
    );  
            
    if(!
    glbEncode[k]) {  
                gb2utf8_char 
    = eval("0x"+
    k);  
                execScript(
    "gb2utf8_char = Chr(gb2utf8_char)""VBScript");  //IE Olny

                glbEncode[k]=escape(gb2utf8_char).substring(1,6);  
            }  
            t[i]
    =glbEncode[k]+t[i].substring(4
    );  
        }  
        gb2utf8_data 
    = gb2utf8_char = null
    ;  
        
    return unescape(t.join("%"
    ));  
    }

    //調用示例

    xhr.open("get""ajax.aspx";  
    xhr.onreadystatechange 
    =
     function(){  
    if (xhr.readyState == 4
    )  
        
    if (xhr.status == 200
    )  
           var text
    =(gb2_To_utf8(xhr.responseBody)); //IE6及以下 Olny,要使用responseBody屬性  

    };  
    xhr.send(
    null);  
    這看起來很失望不是嗎,IE7就不奏效,當然,還有其他更普遍的方法。不過,如果儘量都用UFT8編碼保存網頁,就沒有這些問題了。

2.運用XHR異步刷新頁面

異步刷新頁面是發生在成功返回後進行。刷新一般是通過操縱DOM模型(Document Object Module文檔對象模型)來實現。DOM模型把文檔結構化,每一個結點都是一個對象,這個對象有屬性和方法,因此可以對結點進行操作,並且立即反應到瀏覽器頁面上。例如:document.creatElement(elementName)方法可以創建一個指定標記名稱的新結點對象,當執行parentElementObject.appendChild(childElenmetObject)方法則可以把一個指定的子結點對象的引用添加到父結點的引用,並且父結點的改變會立即反應到頁面上。

實際上,刷新時通常用到的是改變結點的內容,使用innerHTML屬性。該屬性可以改變並立即呈現在結點標記之間的HTML片段內容。

<html>
<head>
<script type="text/javascript">
var xhr
=creatXMLHttpReauest() //調用創建XHR對象的封裝函數,代碼略
xhr.open("get","ajax.aspx?arg=a&arg=b"); 
xhr.onreadystatechange
=function() { 
    
if(xhr.readyState==4) {
        
if(xhr.status==200) {
            document.getElementById(
"div1").innerHTML=xhr.responseText;
        }      
    }                
}
xhr.send(
null);
</script>
</head>
<body>
<div id="div1"></div>
</body>
</html>

運行的結果是,頁面顯示了ajax.aspx頁面返回結果,並且把HTML代碼解釋呈現在頁面上。如果沒有這段JS代碼,頁面將是空白。如果把上面的代碼寫成一個函數,並且在頁面上運行後用一個事件觸發調用,效果很明顯,就是在不知覺的情況下,頁面內容發生了變化,這就是異步刷新。

index.html 頁面代碼

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>ajax</title>
<script type="text/javascript">
function creatXMLHttpReauest()
{
    try{return new ActiveXObject('Msxml2.XMLHTTP'); /*IE5.0*/}catch(e){}
    try{return new ActiveXObject('Microsoft.XMLHTTP'); /*IE5.5*/}catch(e){}
    try{return new XMLHttpRequest();/*IE7及其他類型瀏覽器*/}catch(e){}
    return null;
}
function getWebRequest()
{
    var xhr=creatXMLHttpReauest() //調用創建XHR對象的封裝函數
    xhr.open("get","ajax.html");
    xhr.onreadystatechange=function()
    {
        if(xhr.readyState==4) {
            if(xhr.status==200) {
           
                document.getElementById("panel1").innerHTML=xhr.responseText;
            }     
        }
    }
    xhr.send(null);                 
}
</script>
</head>
<body>
<span id="panel1">1233</span>
<input type="button" οnclick="getWebRequest()" value="進行異步請求"/>
</body>
</html>

ajax.html頁面代碼,注意將這個文件用UTF8編碼保存,否則會出現中文亂碼。

<h3>異步請求完成,這是來自響應頁面的內容</h3>

 3.XHR的調用的探討

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章