关于Ajax的一揽子工程(1)
关于Ajax的一揽子工程
--异步数据交互
/*内部培训用的,写得非常基础*/
Ajax不仅仅就是XMLHttpRequest这么简单。
在以前,我们写客户端的代码一般是实现几个效果,做一下数据校验这些简单的事情。那时,客户端的代码一般不会太多,也不会很复杂,而且大部分情况下这些代码并不是业务过程的核心代码,一定程序上,这些代码是可有可无的,如果它们不能很好的工作,也并不会影响大局。
但现在情况不同了,我们用Ajax做的工程,一方面客户端代码量急骤增加。代码量急聚增中后,代码的管理难度急骤增加,这时我们就需要用到一些工程化管理方法来组织我们的代码,所以我们可以看到很多成熟的Ajax客户端框架都采用了模拟面向对象的方式来实现。另一方面,客户端的不少代码直接和我们的业务逻辑相关,可以说客户端的脚本直接决定我们的程序能不能完成任务,这样的话,我们在写客户端脚本时,不能不更加小心,由此,我们可以看到,Ajax已经涌现出了不少的设计模式,人们也更加重视对Web standard的遵守,也更加重视的cross-browser(跨浏览器)能力。
因此,可以说关于Ajax,有一揽子注意事项,有一揽子工程。
今天我们先来解决最基础的问题:异步数据交互方式。
总的来说,常用的异步数据交互方式有二种:frameset/iframe和XMLHttpRequest。
frameset的一般做法是在框架中做一个高度或宽度为0的隐藏帧。代码如下:
<frameset rows="100%,0" frameborder="0">
<frame name="displayFrame" src="display.htm" noresize="noresize" />
<frame name="hiddenFrame" src="about:blank" noresize="noresize" />
</frameset>
<frame name="displayFrame" src="display.htm" noresize="noresize" />
<frame name="hiddenFrame" src="about:blank" noresize="noresize" />
</frameset>
然后再用脚本操作hiddenFrame负责刷数数据,并把返回给自己的内容更新到显示帧。比如在显示页面的一个按钮注册如下click事件处理程序:
function requestCustomerInfo() {
var sId = document.getElementById("txtFormFiled").value;
top.frames["hiddenFrame"].location = "GetNewData.aspx?id=" + sId;
}
然后,隐藏的帧去获取数据,获取数据之后,同样通过脚本来更新显示帧中的内容:var sId = document.getElementById("txtFormFiled").value;
top.frames["hiddenFrame"].location = "GetNewData.aspx?id=" + sId;
}
<script type="text/javascript">
window.onload = function () {
var divInfoToReturn = document.getElementById("divInfoToReturn");
top.frames["displayFrame"].document.getElementById("divInfo").innerHTML = divInfoToReturn.innerHTML;
};
</script>
</head>
<body>
<div id="divInfoToReturn"><%=Your return content%><div>
</body>
采用这种方式实现post比较的麻烦,需要用DOM操作把显示帧中的form以及form中的值都复制一份到隐藏帧,再通过隐藏帧来代替显示帧提交表单,一般是通过显示帧中的表单提交按钮的onclick事件截获表单提交事件来实现。window.onload = function () {
var divInfoToReturn = document.getElementById("divInfoToReturn");
top.frames["displayFrame"].document.getElementById("divInfo").innerHTML = divInfoToReturn.innerHTML;
};
</script>
</head>
<body>
<div id="divInfoToReturn"><%=Your return content%><div>
</body>
HTML4.0以后我们可以采用iframe,iframe不需要帧集,可以放在文档的任何地方,可以通过脚本创建,所以它更灵活。
function createIFrame() {
var oIFrameElement = document.createElement("iframe");
oIFrameElement.width=0;
oIFrameElement.height=0;
oIFrameElement.frameBorder=0;
oIFrameElement.name = "hiddenFrame";
oIFrameElement.id = "hiddenFrame";
document.body.appendChild(oIFrameElement);
oIFrame = frames["hiddenFrame"];
}
var oIFrameElement = document.createElement("iframe");
oIFrameElement.width=0;
oIFrameElement.height=0;
oIFrameElement.frameBorder=0;
oIFrameElement.name = "hiddenFrame";
oIFrameElement.id = "hiddenFrame";
document.body.appendChild(oIFrameElement);
oIFrame = frames["hiddenFrame"];
}
其它方面来讲,使用iframe和使用帧差别不大。
虽然Ajax流行之后,人们已经更倾向于使用XMLHttpReqeust,但隐藏帧和iframe技术仍然是一项可用的技术(我就曾采用这种技术实现过不刷新的聊天室),而且由于帧能维护浏览器的历史记录,所以现在大型的Ajax应用都综合采用XMLHttp和帧,才尊重人们的“前进”、“后退”按钮习惯。
从IE5.0开始,主流的浏览器现在基本都能很好的支持XMLHttp对象。因为XMLHttp对象是一个Javascript的对象,它更易于创建和管理(特别是在一个JS类库中),也更符合程序员编辑习惯(和帧方式相比,必竟帧是界面的东西)。
不幸的是不同的浏览器实现XMLHttp的方式并不一样(IE用ActiveX方式,而FireFox/Safari/Opera则是XMLHttpRequest),所以,为了cross-browser,我们创建XMLHttp对象的代码可能要写得复杂一点。
//众多的ActiveX的XMLHttp版本
var XML_VERS = ["MSXML2.XmlHttp.5.0", "MSXML2.XmlHttp.4.0",
"MSXML2.XmlHttp.3.0", "MSXML2.XmlHttp",
"Microsoft.XmlHttp"];
function XmlHttp(){}
//创建Requestor
XmlHttp.createRequest = function () {
if (XMLHttpRequest) {//Non-IE
return new XMLHttpRequest();
}
else if (ActiveXObject) {//IE
for( var i = 0 ; i < XML_VERS.length ; i ++){
try{
var oXmlHttp = new ActiveXObject(XML_VERS[i]);
return oXmlHttp;
} catch (e){}
}
}
};
var XML_VERS = ["MSXML2.XmlHttp.5.0", "MSXML2.XmlHttp.4.0",
"MSXML2.XmlHttp.3.0", "MSXML2.XmlHttp",
"Microsoft.XmlHttp"];
function XmlHttp(){}
//创建Requestor
XmlHttp.createRequest = function () {
if (XMLHttpRequest) {//Non-IE
return new XMLHttpRequest();
}
else if (ActiveXObject) {//IE
for( var i = 0 ; i < XML_VERS.length ; i ++){
try{
var oXmlHttp = new ActiveXObject(XML_VERS[i]);
return oXmlHttp;
} catch (e){}
}
}
};
之后,我们就可以通过 var oXmlHttp = XmlHttp.createRequest();这样的语句来创建cross-browser的XmlHttp请求对象了。
之后,我们就可以通过 var oXmlHttp = XmlHttp.createRequest();这样的语句来创建cross-browser的XmlHttp请求对象了。
下面我们以一个Get请求为例说明使用XmlHttp的步骤:
//发送一个get请求
function getRequest(){
var oXmlHttp = XMLHttp.createRequest();//创建XmlHttp对象
oXmlHttp.open("get","server_url.aspx?p=params",true);
//open(方式,路径,是否异步请求)
oXmlHttp.onreadystatechange = function(){
//注册状态变化时的处理函数
if(oXmlHttp.readyState == 4){
//readyState有5种:
//0-未初始化,1-载入中,2-已载入,3-交互中,4-完成
if(oXmlHttp.status == 200){
//status是http状态码,404为资源未找到,203为重定向,401请求验证等等,
//如果没有通过IIS等Web服务宿主,这个状态码可能没有
//200表示一切正常,可以正常处理结果
handleResult(oXmlHttp.responseText);//responseText返回响应文本,
//也可用responseXML返回响应的XML,结果为XMLDOM对象
}
else{
handleError(oXmlHttp.statusText);//statusText为状态结果,非IE可能没有这个属性
}
}
}
oXmlHttp.send(sBody);//一切准备工作做好后,发送请求
}
function getRequest(){
var oXmlHttp = XMLHttp.createRequest();//创建XmlHttp对象
oXmlHttp.open("get","server_url.aspx?p=params",true);
//open(方式,路径,是否异步请求)
oXmlHttp.onreadystatechange = function(){
//注册状态变化时的处理函数
if(oXmlHttp.readyState == 4){
//readyState有5种:
//0-未初始化,1-载入中,2-已载入,3-交互中,4-完成
if(oXmlHttp.status == 200){
//status是http状态码,404为资源未找到,203为重定向,401请求验证等等,
//如果没有通过IIS等Web服务宿主,这个状态码可能没有
//200表示一切正常,可以正常处理结果
handleResult(oXmlHttp.responseText);//responseText返回响应文本,
//也可用responseXML返回响应的XML,结果为XMLDOM对象
}
else{
handleError(oXmlHttp.statusText);//statusText为状态结果,非IE可能没有这个属性
}
}
}
oXmlHttp.send(sBody);//一切准备工作做好后,发送请求
}
XMLHttp对象发送post请求相对来说,要简单一些:
//把表单的所有域组织成key1=value1&key2=value2...的方式
function getRequestBody(oForm){
var aParams = new Array();
for( var i = 0 ;i < oForm.elements.length ; i ++){
var sParam = encodeURIComponent( oForm.element[i].name);//用encodeURIComponent过滤URL字符
sParam += “=”;
sParam += encodeURIComponent( oForm.elements[i].value);
aParams.push(sParam);
}
return aParams.join(“&”);
}
//send a post request
function sendRequest(){
var oForm = document.forms[0];
var sBody = getRequestBody(oForm);//获得请求体
var oXmlHttp = XMLHttp.createRequest();
oXmlHttp.open("post",oForm.action,true);//方式设成post
oXmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded");//设置正确的post的Content-Type
oXmlHttp.onreadystatechange = function(){
if(oXmlHttp.readyState == 4){
if(oXmlHttp.status == 200){
handleResult(oXmlHttp.responseText);
}
else{
handleError(oXmlHttp.statusText);
}
}
}
oXmlHttp.send(sBody);//发送请求时带上请求体
}
使用XMLHttp就这么简单,但是要注意的是Javascript中的same origin policy,就是不同域名的对象之间不能互访(包括二级域名),所以不要在www.site1.com的页面中用XmlHttp请求www.site2.com中的地址。虽然用帧可以在浏览器中同时请求多个域的页面,但它们之间并不能互访,解决方法后面内容再进行探讨。
再一个就是服务器向客户端响应内容时,最好在响应头中加上Cache-Control : no-cache设置,以解决缓存机制带来的不更新数据问题。
//C#代码
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetCacheability(HttpCacheability.NoCache);
异步交互的方法除了以上两种,还可以使用XMLDOM对象来进行,不过XMLDOM只支持获取XML数据,下篇我们再介绍XMLDOM对象,以及脚本中的XML/XPath/XSLT技术。
(未完待续......)