在Ajax技术出现之前,客户端浏览器与服务器之间的交互是非常传统的方式,每一次,浏览器向服务器发送一个请求,服务器接受并处理,返回相对应的处理结果给浏览器,浏览器接收服务器的返回结果,重新加载新的结果,这样的交互方式方式,用户需要花费一定的时间来每一次等待页面的重新加载,以求获取服务器的响应,如果网络不给力或者加载的对象比较大,需要花费一定的时间,那么,用户就并需花费大量的时间在等待上面。
为了避免这种无谓的等待跟提高用户的操作体验,微软第一个站出来,开发了XMLHttpRequest Object,用以实现浏览器与服务器之间的异步通信,进行数据交互,很快,这种方法被大量的采用和广泛的应用,现在所有主流的浏览器都支持了这样的交互方式,通过XMLHttpRequest Object.
Microsoft最初开发的XMLHttpRequest是基于ActiveXObject控件的,与其它的主流浏览器不同(其它的浏览器都是内置本地Javascript支持XMLHttpRequest Object),所以在具体的跨浏览器开发的时候,需要特别留意这一点。尽管在具体的实现细节上,旧的IE浏览器(IE7之前)与其它的主流浏览器不同,但是庆幸的是大家基于这个XMLHttpRequest Object与服务器进行交互的方式确实基本相同,都是采用相同的方法跟属性,这也给我们跨浏览器操作带了极大的便利性。
这里我们简单的介绍一下XMLHttpRequest Object的一些属性,方法,以及如何利用这个Object实现与浏览器的异步操作。
旧版本IE下创建XMLHttpRequest Object
在IE7之前,XMLHttpRequest Object是通过ActiveXObject来实现,方法可以参考如下:
function getXMLHttpRequest() { var versions = [‘MS2XML.XMLHTTP.6.0‘, ‘MS2XML.XMLHTTP.3.0‘, ‘MS2XML.XMLHTTP‘, ‘Microsoft.XMLHTTP‘]; for (var i = 0; i < versions.length; i++) { try { return new ActiveXObject(versions[i]); } catch (e) { continue; } } };
IE7以及其它现代浏览器下创建XMLHttpRequest Object
在IE7+以及其它的现代浏览器中,可以简单地使用以下的语句来创建XMLHttpRequest Object
var xhr = new XMLHttpRequest();
跨浏览器实现
综上所述,我们可以用以下的方法来实现跨浏览器创建XMLHttpRequest Object
function getXMLHttpRequest() { if (typeof XMLHttpRequest !== ‘undefined‘) { return new XMLHttpRequest(); } else { var versions = [‘MS2XML.XMLHTTP.6.0‘, ‘MS2XML.XMLHTTP.3.0‘, ‘MS2XML.XMLHTTP‘, ‘Microsoft.XMLHTTP‘]; for (var i = 0; i < versions.length; i++) { try { return new ActiveXObject(versions[i]); } catch (e) { continue; } } } };
XMLHttpRequest与服务器通信三部曲
XMLHttpRequest Object实现与服务器的通信交互,主要是通过以下的三个步骤来实现:
- 创建XMLHttpRequest Object
- XMLHttpRequest.open(Method, URL, Asyn),该方法有三个参数,第一个是request method,主要是通过GET/POST两种方式,第二个参数是请求的URL,但是必须是与当前的页面处于相同的Domain,第三个是布尔变量,true表示有异步请求,false表示为同步请求,客户端必须等待服务器返回加载完毕之后,才能继续之下往下的操作
- XMLHttpRequest.send(data),该方法有一个参数,如果没有参数传递给服务器,设置为null
XMLHttpRequest Response
当XMLHttpRequest发送请求上服务器,服务器响应并处理完成之后,就会把处理的结果返回给浏览器,我们可以通过XMLHttpRequest Object的一些方法和属性来获取返回的操作结果。
我们可以通过XMLHttpRequest的status, statusText, readyState, responseText以及responseXML属性来查看返回的状态跟结果。
当我们发送请求上服务器之后,我们可以通过readyState的属性来监听当前的状态,readyState总过有以下5ge状态:
- 0 : 还没有进行任何的初始化动作,open method还没有被调用
- 1 : open method被调用,但是请求还没有send出去
- 2 : 调用send method发送请求
- 3 : 数据加载当中
- 4 : 请求完成
当readyState在不同的状态之间切换的时候,会触发onreadystatechange事件,我们可以通过绑定这个事件,对请求的响应状态进行实时的监控:
window.onload = function () { var xhr = getXMLHttpRequest(); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { } } };
通常我们最为关心就是当readyState为4的情况,此时我们可以通过查看当前的HTTP status code,来判定请求是否成功,以下是我们较为常用的status code
- 200 <= xhr.status < 300,当satus code在这个区间的时候,表示请求成功
- 304,这个代码表示not modified since last request, the response will get from browser personal cache,依然表示一个成功的请求
- 另外有一种情况我们需要留意,当我们请求一个本地文件(protocol为file://)的时候,此时的status code返回的是undefined
- 另外一个比较特殊的情况是,当Safari浏览器,the response is not modified since last request,这种情况下它返回的并不是304,而是一个undefined
因此我们可以通过以下的代码还检验一个HTTP请求是否成功:
function httpSuccess(xhr) { return (200 <= xhr.status < 300) || xhr.status === 304 || (xhr.location.host.protocol === ‘file://‘ && xhr.status === undefined) || (userAgent.indexOf(‘Safari‘) !== -1 && xhr.status === undefined); }
我们一般不通过statusText属性来判断当前的请求是否成功,因为不同的浏览器有不同实现,对于相同的结果,可能返回不同的描述。
我们可以通过responseText跟responseXML这两个属性来获取当前返回的内容,无论content-type为何值,我们都可以通过responseText来获取当前的结果,但是responseXML为null,如果当前的content-type不是text/xml或者application/xml.
window.onload = function () { var xhr = getXMLHttpRequest(); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (httpSuccess(xhr)) { console.debug(xhr.responseText); } } } };
序列化请求数据
当我们发送一个请求上服务器的时候,我们通常会向服务器发送额外的请求数据,这个时候我们就需要先将请求数据进行格式化,把它转变成服务器可以处理的形式,通常我们把这个过程称之为序列化。
在客户端,我们通常是以以下的两种形式向服务器提交请求参数:
- JSON格式 : {‘userName‘ : ‘AndyLuo‘, ‘title‘ : ‘Software Engineering‘}
- 表单数据 : [userNameElem, titleElem]
通过序列化我们最终需要把它们转换成诸如 https://www.someurl.com?name1=value1&name2=value2&name3=value3的形式
function serialize(data) { var rtnValue = ‘‘; if (Object.prototype.toString.call(data) === ‘[Object Array]‘) { // handle form elements case for (var i = 0; i < data.length; i++) { var elem = data[i]; rtnValue = addUrlParameter(‘‘, elem.name, elem.value); } } else { for (var k in data) { rtnValue = addUrlParameter(‘‘, k, data[k]); } } return rtnValue; } function addUrlParameter(url, name, value) { if (url.indexOf(‘?‘) == -1) { url += ‘?‘; } else { url += ‘&‘; } url += encodeURIComponent(name) + ‘=‘ + encodeURIComponent(value); return url; }
HTTP Header
我们可以通过xhr.setRequestHeader(hdrName, hdrValue)来订制header value,也可以通过xhr.getResponseHeader(hdrName)以及xhr.getAllResponseHeaders()来获取服务器响应的header头部信息。
xhr.setRequestHeader(‘Content-Type‘, ‘application/x-www-form-urlencoded‘);
xhr.setRequestHeader(‘Content-Type‘, ‘application/xml‘);
xhr.setRequestHeader(‘userName‘, ‘AndyLuo‘);
xhr.getResponseHeader(‘userName‘);
xhr.getALLResponseHeaders();
另外,xhr还提供了一个非常有用的方法overwriteMimeType,我们可以通过修改MIME类型以获得正确的返回,比如,当前的服务器返回的是一个XML数据,但是它的content-type却是设置成了text/plain,这种情况之下,responseXML将为null,我们就可以通过overwriteMimeType(‘text/xml‘)来对返回的content type进行修改以得到我们预期的结果。
GET/ POST 方式请求数据
window.onload = function () { var xhr = getXMLHttpRequest(); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (httpSuccess(xhr)) { console.debug(xhr.responseText); } } } xhr.open(‘GET‘, ‘/someurl/somepage?param1=value1¶m2=value2‘, true); xhr.send(null); };
window.onload = function () { var xhr = getXMLHttpRequest(); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (httpSuccess(xhr)) { console.debug(xhr.responseText); } } } xhr.open(‘POST‘, ‘/someurl/somepage‘, true); xhr.send(‘param1=value1¶m2=value2‘); };
XMLHttpRequest Level 2
随着XMLHttpRequest技术的不同发展,W3C起草了XMLHttpRequest Level 2 Spec,给XMLHttpRequest带了给多特性和可能性,由于尚处于起草阶段,各个浏览器对它的支持也是很有限。
XMLHttpRequest Level 2的其中一个亮点之一就是引入FormData对象,再POST方法请求数据的时候不,可以方便的对表单数据进行操作,其具体的用法有以下两种方式:
- FormData.append(name, value)
- new FormData(formElement)
var formData = new FormData(); formData.append(‘userName‘, ‘AndyLuo‘); xhr.send(formData); xhr.send(new Formdata(document.forms[0]));
另一个值得一提的是,Level 2引进了以下的event事件:
- loadStart : 当客户端接收到第一个字节的时候,触发此事件
- progress : 当客户端持续接收到一个或者多个数据的时候,触发此事件
- error : 当处理请求出现错误的时候
- abort : 当取消当前请求的时候
- load : 请求完成的时候
- loadEnd : 请求结束的时候触发此事件
AJAX using XMLHttpRequest
前面我们提到了传统的浏览器服务器数据交互的模式,用户提交一个请求,等待服务器处理,服务器处理完请求返回数据给浏览器,浏览器重新加载页面显示结果。这样的交互模式并非十分友好,有的时候我们仅仅需要服务返回一点点的信息,但是我们还是一样要经历一系列的动作和等待,而且这这个这个过程中,我们除了等待什么事情也做不了,对于当前的操作页面也完全失去了控制。
我们希望有这样一种方式,当我们需要服务器信息的时候,我们点击页面中的某个按钮或者链接,向服务器提出数据请求,然后我们保留在当前页面继续下面的操作,当服务器返回数据的时候,我们可以很方便的把数据更新到当前页面合适的位置,这个时候,AJAX就应运而生了。
AJAX是Asynchronize JavaScript and XML的缩写,是一种实现客户端与浏览器实现异步操作的技术,底层实现方式就是利用XMLHttpRequest Object.
由于AJAX的应用非常广泛,为了简化我们代码的开发,我们可以把它开发成为一个通用的module,后续工作中,我们只需要通过这个module就可以很方便的实现AJAX的操作,具体如下所示:
function ajax (options) { options = { url : options.url || ‘‘, method : options.method || ‘POST‘, type : options.type || ‘xml‘, asyn : options.asyn || true, timeout : options.timeout || ‘‘, onSuccess : options.onSuccess || function () {}, onError : options.onError || function () {}, onComplete : options.onComplete || function () {}, onTimeout : options.onTimeout || function () {}, data : options.data || {} }; var requestDone = false; try { parseInt(timeout); setTimeout(function() { requestDone = true; options.onTimeout(); }, timeout * 1000); } catch (e) {} var xhr = createXHR(); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && !requestDone) { if (httpSuccess(xhr)) { options.onSuccess(httpData(xhr, options.type)); } else { options.onError(httpData(xhr, options.type)); } options.onComplete(); xhr = null; } }; if (options.method.toLowerCase() === ‘post‘) { xhr.open(options.method, options.url, options.aysn); xhr.send(serialize(options.data)); } else { options.url = addURLParameters(options.url, serialize(options.data)); xhr.open(options.method, options.url, options.aysn); xhr.send(null); } }; function createXHR() { if (typeof XMLHttpRequest !== undefined) { return new XMLHttpRequest(); } else { var versions = [‘MS2XML.XMLHTTP.6.0‘, ‘MS2XML.XMLHTTP.3.0‘, ‘MS2XML.XMLHTTP‘, ‘Microsoft.XMLHTTP‘]; for (var i = 0; i < versions.length; i++) { try { return new ActiveXObject(versions[i]); } catch (e) { continue; } } } }; function httpSuccess (xhr) { try { return (200 <= xhr.status < 300) || (xhr.status === 304) || (!xhr.status && location.protocol === ‘file:‘) || (window.userAgent.indexOf(‘Safari‘) !== -1 && typeof xhr.status === undefined); } catch (e) { return false; } return false; }; function httpData (xhr, type) { var contentType = xhr.getResponseHeader(‘Content-Type‘); var isXMLType = !type && contentType && contentType.indexOf(‘xml‘) >= 0; var data = (type === ‘xml‘) || isXMLType ? xhr.responseXML : xhr.responseText; if (type === ‘script‘) { eval.call(window, data); } return data; }; function serialize(data) { var results = []; if (Object.prototype.toString.call(data) === ‘[Object Array]‘) { for (var i = 0; i < data.length; i++) { data.push(encodeURIComponent(data[i].name) + ‘=‘ + encodeURIComponent(data[i].value)); } } else { for (var key in data) { data.push(encodeURIComponent(key) + ‘=‘ + encodeURIComponent(data[key])); } } return results.join(‘&‘); } function addURLParameters(url, paramStr) { if (url.indexOf(‘?‘) === -1) { url += ‘?‘; } else { url += ‘&‘; } return url + paramStr; }
下面是一个简单的使用例子:
<!DOCTYPE html> <html> <head> <title>AJAX DEMO</title> <script type=‘text/javascript‘ src=‘ajax.js‘></script> </head> <body> <div id=‘weather‘> What‘s the weather like today? <input type=‘button‘ id=‘queryBtn‘ name=‘queryBtn‘ value=‘Query‘ /> </div> <div id=‘console‘> Today‘s Weather:<span id=‘result‘></span> </div> <script type="text/javascript"> window.onload = function () { var queryBtn = document.getElementById(‘queryBtn‘); queryBtn.addEventListener(‘click‘, function() { ajax({ url : ‘<replace your domain url here>‘, type : ‘text‘, onSuccess : function (data) { var result = document.getElementById(‘result‘); result.innerHTML = data; }, onError : function (data) { console.debug(‘fail‘); } }); }, false); }; </script> </body> </html>
运行结果如下所示:
原文:http://www.cnblogs.com/andycbluo/p/5162931.html