XMLHttpRequest——Ajax 时代的到来
开发 Ajax 应用程序的难度在于,需要对 DOM、CSS 和 JavaScript 都很熟悉。
本文目的不在于讨论如何去使用 XMLHttpRequest 对象,因为现在的 Ajax 框架很容易做到,只是从底层去看那些 Ajax 框架。像下面的代码,用 jQuery 只要一两个语句就搞定了。但这不能说明了解 XMLHttpRequest 和它的演化,一点意义都没有。
var oXHR = new XMLHttpRequest();
oXHR.onreadystatechange = function() {
// TODO
}
oXHR.open("GET", "gAddress.txt", true);
oXHR.send(null);
本文内容
- 引入
- XMLHttpRequest 概述
- XMLHttpRequest 演化
- 演示:使用 XMLHttpRequest 对象获取和显示来自服务器的数据
引入
隐藏 iframe 技术是第一个异步模型。但它几个缺点:
- 无法向域外的服务器发送请求。这是因为受到浏览器安全机制限制,JavaScript 只能与同一个域的表单交互,即使是来自于子域的页面也无法访问(如 p2p.wrox.com 与 www.wrox.com)。
- 对背后所发生的 HTTP 请求缺乏足够的信息。它完全依赖于返回的正确页面。也就是说,如果隐藏帧的页面加载失败,不会向用户提示出错信息。主页面要么等待直到调用适当的 JavaScript 函数,要么设置一个较长的超时时间,最后,实在不能成功载入页面,只能显示一条信息,给用户一个安慰。
还好,我们有另一个选择——XMLHttpRequest。可以解决隐藏帧技术存在的问题。
XMLHttpRequest 概述
XMLHttpRequest 解决了隐藏 iframe 技术什么问题:
- 提供足够的 HTTP 请求信息。回想一下现在的 Ajax 框架。不仅可以访问请求和响应首部,还能访问 HTTP 状态码,这使得可以判断请求处理是否成功。
XMLHttpRequest 也有不足之处:
- 不像隐藏帧技术,当发出调用时,并没有浏览器的历史记录保存下来。也就是说,浏览器的后退和前进按钮无效。因此,很多 Ajax 应用程序将 XHR 和隐藏帧技术结合起来,创建一个更加可用的用户界面;
- 如果用户将你的页面设置为特定的安全区域,该区域禁用 ActiveX 控件,这只在 IE 上,将无法访问 XHR 对象。此时,只能使用隐藏帧技术。
- XHR 在跨域通信时也会受到限制。不过,这可以通过服务器代理解决。
XMLHttpRequest 演化
- 微软在 IE 5.0 引入 MSXML ActiveX 程序库,用于创建 XMLHttp 对象,使得开发人员在应用程序的任何地方初始化 HTTP 请求。微软直到 IE 7 才创建他们自己的原生 XMLHttpRequest 对象。虽然,微软的 IE 5.5/6 支持 XMLHttpRequest,但是没有这个对象的本机版本。
- 接下来,Mozilla Firefox 也实现了 XMLHttp 功能。他们创建了一个原生的 JavaScript 对象 XMLHttpRequest。
- 之后,Safari 1.2 和 Opera 7.6 浏览器也复制了 Mozilla 的实现。
现在,这四种浏览器都提供了对原生 XMLHttpRequest 对象的支持。
演示:使用 XMLHttpRequest 对象获取和显示来自服务器的数据
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>My First Ajax Script</title>
<script type="text/javascript">1:
2: window.onload = initAll;
3: var oXHR = false;4:
5: function initAll() {6: document.getElementById("makeTextRequest").onclick = getNewFile;7: document.getElementById("makeXMLRequest").onclick = getNewFile;8: }
9:
10: function getNewFile() {11: makeRequest(this.href);12: return false;13: }
14:
15: function makeRequest(url) {16: if (window.XMLHttpRequest != undefined) {17: oXHR = new XMLHttpRequest();18: }
19: else {20: if (window.ActiveXObject) {21: var aVersions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0"];22: for (var i = 0; i < aVersions.length; i++) {23: try {24: oXHR = new ActiveXObject(aVersions[i]);25: }
26: catch (e) { }27: }
28: }
29: }
30:
31: if (oXHR) {32: oXHR.onreadystatechange = function() {33: if (oXHR.readyState == 4) {34: var outMsg;35: if (oXHR.status == 200 || oXHR.status == 304) {36: outMsg =
37: (oXHR.responseXML && oXHR.responseXML.contentType == "text/xml") ? oXHR.responseXML.getElementsByTagName("choices")[0].textContent : oXHR.responseText;38: }
39: else {40: outMsg = "请求发生错误...XHR 状态: " + oXHR.status;41: }
42: document.getElementById("updateArea").innerHTML = outMsg;43: }
44: };
45: oXHR.open("GET", url, true);46: oXHR.send(null);47: }
48: else {49: document.getElementById("updateArea").innerHTML = "不能创建一个 XMLHttpRequest 对象.";50: }
51: }
52:
</script>
</head>
<body>
<p>
<a id="makeTextRequest" href="gAddress.txt">请求 TXT 文件</a><br />
<a id="makeXMLRequest" href="us-states.xml">请求 XML 文件</a></p>
<div id="updateArea">
</div>
</body>
</html>
运行界面:
图1 初始界面
图2 点击“请求 TXT 文件”后
以下几点值得说明:
1,第 12 行,当函数返回时,告诉浏览器我们不希望装载新的页面;
2,第 16 ~ 29 行,为兼容浏览器尝试创建 XHR 对象,看上去有点麻烦,其实你早以不必这么写。
"oXHR = new ActiveXObject(…)" 是 IE 7 前的版本,其中,MSXML2.XMLHttp.6.0 是针对 IE 9,其他版本的 IE,微软推荐使用 3.0 版本;
"oXHR = new XMLHttpRequest()" 支持现在所有浏览器,所以可以一直这样创建 XHR 对象。
3,第32、44、45 行,当创建一个 XHR 对象后,总是要做三件事,或者说至少两件:
- 设置 onreadystatechange 事件。
- 调用 open() 创建请求。
open(request type, URL, async)
1)请求类型(request type):字符串。GET 或 POST。
2)地址URL:字符串。请求的 URL。
3)异步async:布尔型。fasle 为同步发送请求;true 为异步发送请求。也就是说,我们是否会等待。
同步请求会让 JavaScript 等待接收到响应后再继续执行剩下代码。因此,若响应时间很长,用户在浏览器收到响应前,将无法与其交互。
Ajax 应用程序最佳实践是,使用异步请求来实现常规数据的获取(GET 方式),使用同步请求与服务器之间发送和接收简单的消息(POST 方式)。
如果使用同步请求,则不用设置 onreadystatechange 事件。
- 调用 send() 发送请求。
4,第 33 ~ 43 行,是客户端获得响应后的处理。
- "oXHR.readyState == 4" 是所有数据都已经收到,连接已经关闭。当然还有其他状态,但是由于 的实现不同,唯一可靠的 readyState 值是4。每当 readyState 值发生变化时,就将触发 readyStateChange 事件。只判断 oXHR.readyState 是不够。
- "oXHR.status == 200" HTTP 响应状态码,表示成功。如果状态为 304,即 "oXHR.status == 304",则 responseText 和 responseXML 属性仍然包含正确的数据。只是该数据来自于浏览器缓存,而不是服务器。