Ajax原理-原生js的XMLHttpRequest對象意義
Ajax是對Asynchronous JavaScript + XML的簡寫,這一技術能夠向伺服器請求額外的資料而無需卸載頁面,會帶來很好的使用者體驗。Ajax技術的核心是XMLHttpRequest(簡稱XHR)對象,XHR為向伺服器發送請求和解析伺服器響應提供了流暢的介面,能夠以非同步方式從伺服器府區區擷取更多資訊而不必重新整理頁面。使用XML對象取得新資料然後在通過DOM將新資料插入到頁面中。
一:XMLHttpRequest對象
IE5是第一款引入XHR對象的瀏覽器,XHR對象是通過MSXML庫中的一個ActiveX對象實現的。因此,在IE中可能會遇到三種不同版本的XHR對象:MSXML2.XMLHttp、MSXML2.XMLHttp.3.0、MSXML2.XMLHttp.6.0 。例如:
function createXHR(){ if(typeof arguments.callee.activeXString != 'string'){ var versions = ['MSXML2.XMLHttp.6.0','MSXML2.XMLHttp','MSXML2.XMLHttp.3.0']; for(var i=0; i<versions.length; i++){ try{ new ActiveXObject(versions[i]); arguments.callee.activeXString = versions[i]; break; }catch(e){ console.log('no'); } } } return new ActiveXObject(arguments.callee.activeXString); }
這個函數會根據IE中可用的MSXML庫的情況建立最新版本的XHR對象,IE7Firefox、Opera、Chrome和Safari都支援原生的XHR對象,在這些瀏覽器中建立XHR對象要這樣:var xhr = new XMLHttpRequest();,加入你想支援IE7及更高的版本,那麼,大可丟掉前面定義的哪個函數,而只用原生的XHR實現。但是,如果你必須要使用IE更早的版本,可以在這個createXHR()函數中加入原生XHR對象的支援。
function createXHR() { if(XMLHttpRequest) { return new XMLHttpRequest(); } else if(ActiveXObject) { if(typeof arguments.callee.activeXString != 'string') { var versions = ['MSXML2.XMLHttp.6.0', 'MSXML2.XMLHttp', 'MSXML2.XMLHttp.3.0']; for(var i = 0; i < versions.length; i++) { try { new ActiveXObject(versions[i]); arguments.callee.activeXString = versions[i]; break; } catch(e) { console.log('no'); } } } return new ActiveXObject(arguments.callee.activeXString); } else { throw new Error("No XHR object available"); } }
二:XHR的用法。
使用XHR對象時,要調用的第一個方法是open(),例如:
xhr.open('get','somfile.txt',false);xhr.send(null);
這裡send()方法,如果不需要向請求主體發送資料,最好傳入null參數,因為這個參數對有些瀏覽器來說是必須的。
由於這次響應式同步的,所以,js代碼會等到伺服器響應之後再繼續執行。收到響應後,響應資料會自動填滿XHR對象的屬性,相關屬性如下:
responseText:作為響應主體被返回的文本;
responseXML:作為相應的內容類型是text/xml或application/xml,這個屬性中將儲存包含著相應資料的XMLDOM文檔;
status:相應的http狀態;
statusText:http狀態的說明。
在接收到響應之後,第一步檢查status屬性,當xhr.status為200時候,說明接受成功,當xhr.status為304的時候,表示請求的資源沒有被修改,可以直接使用瀏覽器中緩衝的版本。代碼如下:
if(xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 304)) { document.getElementById("myDiv").innerHTML = xhr.responseText; } else { "Request was unsuccessful: " + xhr.status; }
非同步用法
大多數情況下我們是要發送非同步請求,才能讓JavaScript繼續執行當前的活動階段。取值如下:
0:未初始化,尚未調用open()方法。
1.啟動。已經調用open()方法,但尚未調用send()方法。
2.發送,已經調用send()方法,為接受到響應。
3.接受。已經接受部分響應資料。
4.完成。全部接受響應的資料,並且可以在用戶端使用。
只要readyState屬性的值發生改變,都會觸發一次onreadyStateChange事件。代碼如下:
function loadXMLDoc() { var xhr = createXHR(); xhr.onreadystatechange = function() { console.log(xhr) if(xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 304)) { document.getElementById("myDiv").innerHTML = xhr.responseText; } else { "Request was unsuccessful: " + xhr.status; } } xhr.open('GET', 'somfile.txt', true); xhr.send(null); }
另外,在接收到響應之前,還可以調用abort()方法,取消非同步請求:xhr.abort();
三:用setRequestHeader設定自訂要求標頭
這個方法接受兩個參數:頭部欄位的名稱和頭部欄位的值。伺服器在接受到這種自訂的頭部資訊之後,可以執行後續操作,建議使用,否則可能會影響到伺服器的響應。
function loadXMLDoc(){ var xhr = createXHR(); xhr.onreadystatechange = function(){ if(xhr.readyState==4 || xhr.status==200){ document.getElementById("myDiv").innerHTML = xhr.responseText; }else{ "Request was unsuccessful: " + xhr.status; } } xhr.open('GET','somfile.txt',true); xhr.setRequestHeader('MyHeader','MyValue'); xhr.send(null); }
四:GET請求
對XHR而言,位於傳入open()方法的URL末尾的查詢字串必須經過正確的編碼才行。
使用GET請求經常會發生的一個錯誤,就是查詢字串格式有問題。查詢字串中每個參數的名稱和值都必須使用encodeURIComponent()進行編碼,然後才能放到URL的末尾,而且所用名-值對都必須由&分割,如下面所示:
<body> name: <input type="text" name="name"> <br> book: <input type="text" name="book"> <br> <input type="button" id="submit" value="submit" ></body><script type="text/javascript"> function createXHR() { if (XMLHttpRequest) { return new XMLHttpRequest(); } else { return new ActiveXObject('Microsoft.XMLHTTP'); } }; //get方式,索引值對加工 function addURLParam(url, name, value) { url += (url.indexOf('?') == -1 ? '?' : '&'); url += encodeURIComponent(name) + '=' + encodeURIComponent(value); return url; } //類比表單提交 function formSubmit() { //擷取文字框節點 var names = document.querySelectorAll('input')[0].value; var books = document.querySelectorAll('input')[1].value; console.log(names) var xhr = createXHR(); xhr.onreadystatechange = function () { if (xhr.readyState == 4 || xhr.status == 200) { console.log(xhr.responseText) } } var url = '01.php'; //添加參數 url = addURLParam(url, 'name', names); url = addURLParam(url, 'book', books); console.log(url) //初始化請求 xhr.open("get", url, true); xhr.send(null); } var sub = document.getElementById('submit'); sub.onclick = function(){ formSubmit(); }</script>
五:POST請求
伺服器對post請求不一樣的,因此,伺服器端必須有程式來讀取發送過來的未經處理資料,並從中解析出有用的部分:首先,將Content-Type頭部資訊設定為application/x-www-form-urlencoded,也就是表單提交時的內容類型,其次是以適當的格式建立一個字串,如果需要將POST資料進行序列化以便伺服器方便處理,就需要XMLHttpRequest2級定義的FormData類型。即:
var data = new FormData();data.append('name','bangbang');... ...xhr.open('post','02.php',true);var form = document.getElementById('form1');xhr.send(new FormData(form));
當然,我們可以用自訂的serialize()函數來處理。
例如:
<body> <form id="form1"> name: <input type="text" name="name"> <br> book: <input type="text" name="book"> <br> <input type="button" id="submit" value="submit"> </form> <div id="con"></div></body><script type="text/javascript"> //序列化函數 function serialize(form) { var parts = [], field = null, option, optValue; for (var i = 0; i < form.elements.length; i++) { field = form.elements[i]; switch (field.type) { case "select-one": case "select-multiple": if (field.name.length) { for (var j = 0; j < field.options.length; j++) { option = field.options[j]; if (option.selected) { optValue = ''; if (optioin.hasAttribute) { optValue = (option.hasAttribute('value') ? option.value : option.text); } else { optValue = (option.attributes['value'].specified ? option.value : option.text); } parts.push(encodeURIComponent(field.name) + '=' + encodeURIComponent(optValue)); } } } break; case undefined: //欄位集 case 'file': //檔案輸入 case 'submit': //提交按鈕 case 'reset': //重設按鈕 case 'button': //自訂按鈕 break; case 'radio': //選項按鈕 case 'checkbox': //複選框 if (!field.checked) { break; } default: if (field.name.length) { parts.push(encodeURIComponent(field.name) + '=' + encodeURIComponent(field.value)); } } } return parts.join('&'); } function createXHR() { if (XMLHttpRequest) { return new XMLHttpRequest(); } else { return new ActiveXObject('Microsoft.XMLHTTP'); } }; function formSubmit() { var xhr = createXHR(); console.log(xhr.readyState); xhr.onreadystatechange = function () { if (xhr.readyState == 4 || xhr.status == 200) { console.log(xhr.responseText); document.getElementById('con').innerHTML = xhr.responseText; } } //初始化請求 xhr.open("post", '02.php', true); xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); var form = document.getElementById('form1'); console.log(serialize(form)); xhr.send(serialize(form)); } var sub = document.getElementById('submit'); sub.onclick = function () { formSubmit(); }</script>