Ajax,是对Asynchronous Javascript+XML的简写。Ajax技术的核心是XMLHttpRequest对象(XHR)。XHR为向服务器发送请求和解析服务器响应提供了流畅的接口。能够以异步方式从服务器取得更多信息,意味着用户单击后,可以不必刷新页面也能取得新数据。
一、XHR对象。
IE5是第一款引入XHR对象的浏览器。在IE5中,XHR对象是通过MSXML库中的一个ActiveX对象实现的。因此,在IE中可能会遇到3种不同版本的XHR对象,
即:MSXML2.XMLHttp、
MSXML2.XMLHttp.3.0、
MSXML2.XMLHttp.6.0。
要使用MSXML库中的XHR对象,需要编写一个函数。这个函数会尽力根据IE中可用的MSXML库的情况创建最新版本的XHR对象。例如:
//适用于IE7之前的版本 function createXHR(){ if(typeof arguments.callee.activeXString != "string"){ var versions = ["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"]; for(var i = 0,len = version.length; i < len; i++){ try{ var xhr = new ActiveXObject(versions[i]); arguments.callee.activeXString = versions[i]; return xhr; }catch (ex){ //code } } } return new ActiveXObject(arguments.callee.activeXString); }
IE7、Firefox、Opera、Chrome和Safari都支持原生的XHR对象。在这些浏览器中创建XHR对象要像下面这样使用XMLHttpRequest构造函数。
var xhr = new XMLHttpRequest();
但是如果要全部兼容,就可以使用以下代码:
function createXHR(){ if(typeof XMLHttpRequest != "underfined"){ //检测原生XHR对象是否存在 return new XMLHttpRequest(); }else if(typeof ActiveXObject != "underfined"){ //检测ActiveX对象是否存在 if(typeof arguments.callee.activeXString != "string"){ var versions = ["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp"]; for(var i = 0,len = version.length; i < len; i++){ try{ var xhr = new ActiveXObject(versions[i]); arguments.callee.activeXString = versions[i]; return xhr; }catch (ex){ //code } } } return new ActiveXObject(arguments.callee.activeXString); }else{ throw new Error("No XHR object available."); } }这个函数中新增的代码首先检测原生 XHR对象是否存在,如果存在则返回它的新实例。如果原生对象不存在,则检测Active对象。如果这两种对象都不存在,就抛出一个错误。写完这个函数,我们就可以使用下面的代码在所有浏览器中创建XHR对象了。
var xhr = new createXHR();
二、XHR的用法:
1、调用第一个方法是open()。它接受三个参数:要发送的请求的类型("get"、“post”等)、请求的URL和表示是否异步发送请求的布尔值。如
xhr.open("get","example.PHP",false);2、发送特定的请求,必须像下面这样调用 send()方法。send()方法接受一个参数,即要作为请求主体发送的数据。如果不需要通过主体发送数据,则必须传入null,因为这个参数对有些浏览器来说是必需的。
xhr.send(null);3、等到服务器响应之后。响应的数据会自动填充 XHR对象的属性,相关的属性简介如下。
responseText:响应主体被返回的文本。
responseXML:如果响应的内容类型是"text/xml"或"application/xml",这个属性中将保存包含着响应数据的XML DOM文档。
status:响应的HTTP状态。
statusText:HTTP状态的说明。
为确保接收到适当的响应,应该像下面这样检查上述这两种状态代码。第一步检查status属性,以确定响应已经成功返回。一般来说,可以将HTTP状态代码为200作为成功的标志。
if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ alert(xhr.statusText); }else{ alert("Request was unsuccessful:" + xhr.status); }
4、检测readyState属性。以上步骤是发送同步请求的方法,但多数情况下,我们还是要发送异步请求,才能让javascript继续执行而不必等待响应。此时,可以检测XHR对象的readyState属性,该属性表示请求/响应过程的当前活动阶段。这个属性可取的值如下:
1:启动。已经调用open()方法,但尚未调用send()方法
3:接收。已经接收到部分响应数据。
4:完成。已经接收到全部响应数据,而且已经可以在客户端使用了。
只要readyState属性的值由一个值变成另一个值时,都会触发一次readyStatechange事件。可以利用这个事件来检测每次状态变化后readyState的值。通常,我们只对readyState值为4的阶段感兴趣。因为这时所有数据都已经就绪。不过,必须在调用open()之前指定onreadystatechange事件处理程序才能确保跨浏览器兼容性。
var xhr = new createXHR(); xhr.onreadystatechange = function(){ if(xhr.readyState == 4){ if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ alert(xhr.statusText); }else{ alert("Request was unsuccessful:" + xhr.status); } } }; xhr.open("get",true); xhr.send(null);三、HTTP头部信息
每个http请求和响应都会带有相应的头部信息,其中有对开发人员有用,有的也没有什么用。
默认情况下,浏览器在发送XHR请求的同时,还会发送下列头部信息。
Accept:浏览器能够处理的内容类型。
Accept-Charset:浏览器能够显示的字符集。
Accept-Encoding:浏览器能够处理的压缩编码。
Accept-Language:浏览器当前设置的语言。
Connection:浏览器与服务器之间连接的类型。
Cookie:当前页面设置的任何Cookie。
Host:发出请求的页面所在的域。
Referer:发出请求的页面所在的域。
User-Agent:浏览器有用户代理字符串。
1、使用setRequestHeader()方法自定义的请求头部信息。这个方法接受两个参数:头部字段的名称和头部字段的值。而且必须在调用open()方法之后且调用send()方法之前调用。即:
xhr.open("get","test.txt",true); xhr.setRequestHeader("MyHeader","MyValue"); xhr.send();2、调用XHR对象的 getResponseHeader()方法并传入头部字段名称,可以取得相应的响应头部信息。而调用 getAllResponseHeaders()方法则可以取得一个包含所有头部信息的长字符串。
var myHeader = xhr.getResponseHeader("MyHeader"); var allHerders = xhr.getAllResponseHeaders();
DEMO:
<!DOCTYPE html> <html> <head> <Meta charset="utf-8"/> <title>ajax demo</title> <script type="text/javascript"> //XHR对象的构造函数 function createXHR(){ if(typeof XMLHttpRequest != "underfined"){ //检测原生XHR对象是否存在 return new XMLHttpRequest(); }else if(typeof ActiveXObject != "underfined"){ //检测ActiveX对象是否存在 if(typeof arguments.callee.activeXString != "string"){ var versions = ["MSXML2.XMLHttp.6.0",len = version.length; i < len; i++){ try{ var xhr = new ActiveXObject(versions[i]); arguments.callee.activeXString = versions[i]; return xhr; }catch (ex){ //code } } } return new ActiveXObject(arguments.callee.activeXString); }else{ throw new Error("No XHR object available."); } } //按扭响应函数:创建xhr对象并进行相应操作 function loadXMLDoc(){ var xhr = new createXHR(); xhr.onreadystatechange = function(){ if(xhr.readyState == 4){ if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ alert("statusText is "+xhr.statusText); alert("responseText is "+xhr.responseText); alert("responseXML is "+xhr.responseXML); alert("status is "+xhr.status); }else{ alert("Request was unsuccessful:" + xhr.status); } } }; xhr.open("get",true); xhr.setRequestHeader("Accept","90arther"); xhr.send(); } </script> </head> <body> <h2>AJAX</h2> <button type="button" onclick="loadXMLDoc()">请求数据</button> <div id="myDiv"></div> </body> </html>
四 、GET请求
GET是最常用的请求类型,最常用于向服务器查询某些信息。对于XHR而言,位于传入open()方法的URL末尾的查询字符串必须经过正确的编码才行。
1、查询字符串中的每个参数的名称和值必须使用encodeURIComponent()进行编码,然后才能放到URL的末尾,而且所有名-值对都必须由和号(&)分隔,如下面的例子所示:
xhr.open("get","example.PHP?name1=value1&name2=value2",true);2、下面这个函数可以辅助向现有URL的末尾添加查询字符串的参数,这个函数接受三个参数:要添加参数的URL、参数的名称和参数的值。这个函数首先检查URL是否包含问号(以确定是否已经有参数存在)。如果没有,就添加一个问号;否则,就添加一个和号。然后,将参数名称和值进行编码,再添加到URL的末尾。最后返回添加参数之后的URL。
function addURLParam(url,name,value){ url += (url.indexOf("?")== -1?"?":"&"); url += encodeURIComponent(name) + "=" + encodeURIComponent(value); return url; }
3、下面是使用这个函数来构建请求URL的示例:
var url = "example.PHP"; url = addURLParam(url,"name","Nicholas"); url = addURLParam(url,"Professional JavaScript"); //初始化请求 xhr.open("get",url,false);四、POST 请求
POST请求的主体可以包含非常多的数据,而且格式不限。在open()方法第一个参数的位置传入"post",就可以初始化一个POST请求,如下面的例子所示:
xhr.open("post",true);
1、默认情况下,服务器对POST请求和提交表单的请求并不会一视同仁。因此,服务器端必须有程序来读取发送过来的原始数据,并从中解析出有用的部分。不过,我们可以使用XHR来模仿表单提交:首先将Content-Type头部信息设置为application/x-www-form-urlencoded,也就是表单提交时的内容类型,其次是以适当的格式 创建一个字符串。
function submitData(){ var xhr = createXHR(); xhr.onreadystatechange = function (event){ if(xhr.readyState == 4){ if((xhr.status >= 200 && xhr.status < 300)||xhr.status == 304){ alert(xhr.responseText); }else{ alert("Request was unsuccessful:"+ xhr.status); } } }; xhr.open("post","postexample.PHP",true); xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); var form = document.getElementById("user-info"); xhr.send(serialize(form)); }2、这个函数可以将ID为"user-info"的表单中的数据序列化之后发送给服务器,而下面的示例PHP文件postexample.PHP就可以通过$_POST取得提交的数据了。
<?PHP header("Content-Type:text/plain");//如果不设置Content-Type头部信息,那么发送给服务器的数据就不会出现在$_POST超级全局变量中。 echo <<<EOF Name:{$_POST['user-name']} Email:{$_POST['user-email']} EOF; ?>
五、浏览器差异
1、超时设定
IE8为XHR对象添加了一个timeout属性,表示请求在等待响应多少毫秒之后就终止。在给timeout设置一个数值后,如果在规定时间内浏览器还没有收到响应,那么就会触发timeout事件,进而会调用ontimeout事件处理程序。
var xhr = createXHR(); xhr.onreadystatechange = function(event){ try{ if(xhr.readyState == 4){ if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ alert(xhr.responseText); }else{ alert("Request was unsuccessful:"+ xhr.status); } } }catch (ex){ //假设由ontimeout事件处理程序处理 } }; xhr.open("get","timeout.PHP",true); xhr.timeout = 1000; xhr.ontimeout = function(){ alert("Request did not not return in a second."); }; xhr.send(null);
2、加载(load)事件
Firefox实现中引入了load事件,用以替代readystatechange事件。响应接收完毕将触发load事件,因此也就没有必要去检查readyState属性了。而onload事件处理程序会接收一个event对象,其target属性就指向xhr对象实例,因而可以访问到XHR对象的所有方法和属性。然而,并非所有浏览器都这个事件实现了适当的事件对象。结果,开发人员还是要像下面这样被迫使用XHR对象变量:
var xhr = createXHR(); xhr.onload = function (event){ if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ alert(xhr.responseTest); }else{ alert("Request was unsuccessful:"+ xhr.status); } }; xhr.open("get","altevents.PHP",true); xhr.send(null);
只要浏览器接收到服务器的响应,不管其状态如何,都会触发load事件。而这意味着你必须要检查status属性,才能确定数据是否真的已经可用了。Firefox、Opera、Chrome和Safari都支持load事件。
3、进度(progress)事件Mozilla为XHR添加了progress事件(直到2008年,还没有其它浏览器实现这个特性)。
onprogress事件处理程序会接收到一个event对象,其target属性是XHR对象,但包含着两个额外的属性:position 和totalSiza。其中,position表示已经接收的字节数,totalSize表示根据content-length响应头部确定的预期字节数。
有了这些我们 就可以制作一个进度指示器了。
var xhr = createXHR(); xhr.onload = function(event){ if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ alert(xhr.responseText); }else{ alert("Request was unsuccessful" + xhr.status); } }; xhr.onprogress = function(event){ var divStatus = document.getElementById("status"); divStatus.innerHTML = "Received" + event.position + "of" + event.totalSize + "bytes"; }; xhr.open("get","inc.txt",true); xhr.send(null);