XMLHttpRequest 对象
IE5是第一款引入XHR对象的浏览器。在IE5中,XHR对象是通过 MSXML 库中的一个 ActiveX 对象实现的。因此,在IE中可能会遇到三种不同版本的XHR对象,即 MSXML2.XMLHttp、MSXML2.XMLHttp.3.0 和 MSXML2.XMLHttp.6.0。要使用 MSXML 库中的XHR对象,需要编写一个函数,例如:
<script type="text/javascript"> //适用于IE7之前的版本 function createXHR() { if (typeof arguments.callee.activeXString != "string") { var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"],i,len; for (i=0,len=versions.length; i<len; i++) { try { new ActiveXObject(versions[i]); arguments.callee.activeXString = versions[i]; break; } catch (ex) { //跳过 } } } return new ActiveXObject(arguments.callee.activeXString); } </script>
这个函数会尽力根据IE中可用的MSXML库的情况创建最新版本的XHR对象。
IE7+、Firefox、Opera、Chrome和Safari都支持原生的XHR对象,在这些浏览器中创建XHR对象要像下面这样使用XMLHttpRequest构造函数。
var xhr = new XMLHttpRequest();
假如你只想支持IE7及更高版本,那么大可丢掉前面定义的那个函数,而只用原生的XHR实现。但是,如果你必须还要支持IE的早期版本,那么则可以在这个createXHR()函数中加入对原生XHR对象的支持。
<script type="text/javascript"> function createXHR() { if (typeof XMLHttpRequest != "undefined") { return new XMLHttpRequest(); } else if (typeof ActiveXObject != "undefined") { if (typeof arguments.callee.activeXString != "string") { var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"],i,len; for (i=0,len=versions.length; i<len; i++) { try { new ActiveXObject(versions[i]); arguments.callee.activeXString = versions[i]; break; } catch (ex) { //跳过 } } } return new ActiveXObject(arguments.callee.activeXString); } else { throw new Error("No XHR object available"); } } </script>
这个函数中新增的代码首先检测原生XHR对象是否存在,如果存在则返回它的新实例。如果原生对象不存在,则检测ActiveX对象。如果这两种对象都不存在,就抛出一个错误。然后,就可以使用下面的代码在所有浏览器中创建XHR对象了。
var xhr = createXHR();
由于其他浏览器中对XHR的实现与IE最早的实现是兼容的,因此就可以在所有浏览器中都以相同方式使用上面创建的xhr对象。
XHR的用法
在使用XHR对象时,要调用的第一个方法是open(),它接受3个参数:要发送的请求的类型(get、post等)、请求的URL和表示是否异步发送请求的布尔值。下面就是调用这个方法的例子。
xhr.open("get","example.php",false);
这行代码会启动一个针对example.php的GET请求。有关这行代码,需要说明两点:一是URL相对于执行代码的当前页面(当然也可以使用绝对路径);二是调用open()方法并不会真正发送请求,而只是启动一个请求以备发送。
要发送特定的请求,必须像下面这样调用send()方法:
xhr.open("get", "example.php", false);
xhr.send(null);
这里的send()方法接收一个参数,即要作为请求主体发送的数据。如果不需要通过请求主体发送数据,则必须传入null,因为这个参数对有些浏览器来说是必需的。调用send()之后,请求就会被分派到服务器。
由于这次请求是同步的,JavaScript代码会等到服务器响应之后再继续执行。在收到响应后,响应的数据会自动填充XHR对象的属性,相关的属性简介如下。
在接收到响应后,第一步是检查status属性,以确定响应已经成功返回。一般来说,可以将HTTP状态代码为200作为成功的标志。此时,responseText属性的内容已经就绪,而且在内容类型正确的情况下,responseXML也应该能够访问了。此外,状态代码为304表示请求的资源并没有被修改,可以直接使用浏览器中缓存的版本;当然,也意味着响应是有效的。为确保接收到适当的响应,应该像下面这样检查上述这两种状态代码:
xhr.open("get", "example.txt", false); xhr.send(null); if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) { alert(xhr.responseText); } else { alert("Request was unsuccessful: " + xhr.status); }
根据返回的状态代码,这个例子可能会显示由服务器返回的内容,也可能会显示一条错误消息。我们建议读者要通过检测status来决定下一步的操作,不要依赖statusText,因为后者在跨浏览器使用时不太可靠。另外,无论内容类型是什么,响应主体的内容都会保存到responseText属性中;而对于非XML数据而言,responseXML属性的值将为null。
有的浏览器会错误地报告204状态代码。IE中XHR的ActiveX版本会将204设置为1223,而IE中原生的XHR则会将204规范化为200。Opera会在取得204时报告status的值为0。
像前面这样发送同步请求当然没有问题,但多数情况下,我们还是要发送异步请求,才能让JavaScript继续执行而不必等待响应。此时,可以检测XHR对象的readyState属性,该属性表示请求/响应过程的当前活动阶段。这个属性可取的值如下。
只要readyState属性的值由一个值变成另一个值,都会触发一次readystatechange事件。可以利用这个事件来检测每次状态变化后readyState的值。通常,我们只对readyState值为4的阶段感兴趣,因为这时所有数据都已经就绪。不过,必须在调用open()之前指定onreadystatechange事件处理程序才能确保跨浏览器兼容性。下面来看一个例子。
var xhr = createXHR(); xhr.onreadystatechange = function(){ 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("get", "example.txt", true); xhr.send(null);
以上代码利用DOM 0级方法为XHR对象添加了事件处理程序,原因是并非所有浏览器都支持DOM 2级方法。与其他事件处理程序不同,这里没有向onreadystatechange事件处理程序中传递event对象;必须通过XHR对象本身来确定下一步该怎么做。
这个例子在onreadystatechange事件处理程序中使用了xhr对象,没有使用this对象,原因是onreadystatechange事件处理程序的作用域问题。如果使用this对象,在有的浏览器中会导致函数执行失败,或者导致错误发生。因此,使用实际的XHR对象实例变量是较为可靠的一种方式。
另外,在接收到响应之前还可以调用abort()方法来取消异步请求,如下所示:
xhr.abort();
调用这个方法后,XHR对象会停止触发事件,而且也不再允许访问任何与响应有关的对象属性。在终止请求之后,还应该对XHR对象进行解引用操作。由于内存原因,不建议重用XHR对象。