在Ajax技术出现以前,客户端浏览器与服务器之间的交互是很是传统的方式,每一次,浏览器向服务器发送一个请求,服务器接受并处理,返回相对应的处理结果给浏览器,浏览器接收服务器的返回结果,从新加载新的结果,这样的交互方式方式,用户须要花费必定的时间来每一次等待页面的从新加载,以求获取服务器的响应,若是网络不给力或者加载的对象比较大,须要花费必定的时间,那么,用户就并需花费大量的时间在等待上面。javascript
为了不这种无谓的等待跟提升用户的操做体验,微软第一个站出来,开发了XMLHttpRequest Object,用以实现浏览器与服务器之间的异步通讯,进行数据交互,很快,这种方法被大量的采用和普遍的应用,如今全部主流的浏览器都支持了这样的交互方式,经过XMLHttpRequest Object.html
Microsoft最初开发的XMLHttpRequest是基于ActiveXObject控件的,与其它的主流浏览器不一样(其它的浏览器都是内置本地Javascript支持XMLHttpRequest Object),因此在具体的跨浏览器开发的时候,须要特别留意这一点。尽管在具体的实现细节上,旧的IE浏览器(IE7以前)与其它的主流浏览器不一样,可是庆幸的是你们基于这个XMLHttpRequest Object与服务器进行交互的方式确实基本相同,都是采用相同的方法跟属性,这也给咱们跨浏览器操做带了极大的便利性。java
这里咱们简单的介绍一下XMLHttpRequest Object的一些属性,方法,以及如何利用这个Object实现与浏览器的异步操做。ajax
旧版本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 Objectapp
var xhr = new XMLHttpRequest();
跨浏览器实现dom
综上所述,咱们能够用如下的方法来实现跨浏览器建立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 || (window.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>
运行结果以下所示: