15.2 AJAX的核心技术
在上面一节中,已经提到了AJAX是一种技术的组合。AJAX是由几种技术组合而 成的。
(1) XmlHttpRequest—XmlHttpRequest对象允许浏览器通过它与Web服务器进行通信。这个对象为页面提供了客户端和服务器端的异步通信功能。在Internet Explorer中,这个功能由“MSXML”ActiveX组件提供;在FireFox中,这个功能由一个叫XmlHttpReqeust的对象来提供。在编写AJAX代码时,需要判断浏览器类型,并为不同浏览器提供不同的XmlHttpRequest的调用代码。
(2)JavaScript—所有的浏览器都支持JavaScript脚本语言。AJAX使用JavaScript脚本语言来操作XmlHttpRequest对象、操作DOM和CSS等。
(3)DHTML/DOM—能正确显示AJAX应用程序页面的浏览器必须要能通过DOM动态更新HTML元素的内容。
(4)使用XML传输数据—虽然也能使用HTML或者其他的格式在客户端和服务器之间传递数据,不过XML显然是标准的做法,并且使用XML容易结构化和层次化数据。
下面就对这几种技术进行简要的介绍。
15.2.1 JavaScript 简介
毫无疑问,JavaScript在AJAX中扮演了指挥的角色。JavaScript负责将各个不同的技术粘合在一起协同工作,组织漂亮的页面。AJAX应用程序在执行时,会预先将客户端所有的代码下载到内存中,下载的内容包括数据、HTML代码和逻辑控制代码。JavaScript就是实现逻辑控制的工具。
JavaScript是一种通用的脚本语言,它与C系列的语言有非常多的相似点。JavaScript语言的特点用一句话概括就是“它是一种通用的,弱类型的,解释型的脚本语言”。
弱类型表明JavaScript的变量可以不显式申明为某种数据类型,例如string类型、int类型等等。同一个变量可以被赋予不同类型的数据。下面的代码是合法的:
var i = 1;
i = 100.05;
i='3.1415926';
变量i首先被赋予了一个整形的值,然后赋予了一个浮点数,最后把一个字符串赋予了它。只有弱类型的语言可以这样做,可以与C#语言进行对比。
解释型脚本语言意味着JavaScript不会被编译为可执行的机器码,解释器直接按照源代码所表述的翻译执行。部署JavaScript时,将JavaScript的代码文件放置在Web服务器上,客户端发出请求时,把JavaScript文件直接通过网络传递给客户端,然后客户端的浏览器对下载的JavaScript代码进行解释并执行预定的操作。还可以在代码中动态地使用表达式,然后调用JavaScript的Eval方法对表达式进行解析,并得到表达式的结果。这种方式会降低程序的运行速度,不过它带来了非常好的灵活性,应该恰当地使用它。
JavaScript是一种通用的语言,它可以用在大多数的编程任务中。例如,JavaScript可以用来编写运行在Windows下的程序,还可以用来编写游戏的脚本,等等。JavaScript语言和大多数通用语言一样,支持多种数据类型—整形数、浮点数、字符串、日期、数组等,它也支持用于文本处理的正则表达式,支持数学的各种函数。使用JavaScript也可以定义结构和类。
在浏览器中,一些浏览器内置的功能,如CSS、DOM和XMLHttpRequest对象暴露给了JavaScript的解释器,因此页面的开发人员可以直接在JavaScript代码中调用这些功能来控制页面。
这里不是详细讲解JavaScript的地方,如果希望了解更多的信息可以查阅相关的书籍。
15.2.2 文档对象模型(DOM)
文档对象模型(DOM),为访问HTML文档中所有的元素提供了方法。在HTML文档中,包括可见的元素,如超级链接、文本输入框(input)、按钮等,不可见的元素,如HTML文档的头部信息、DIV区域、SPAN区域等,都可以在DOM中以类似的方式访问。与DOM访问有关的对象有两个:
l 浏览器对象模型
l 文档对象模型
1.浏览器对象模型
浏览器对象模型代表了浏览器自身。一般来说,浏览器对象模型包括了一个根节 点—Window对象。Window对象包含了几个其他对象的引用,包括navigator、history、location、Frames和document(文档)对象。document对象包括了页面的元素,它就是一个DOM对象的实例引用。图15-3所示是浏览器对象模型的结构。
图15-3 浏览器对象模型
在使用中常常分不大清楚浏览器对象模型和文档对象模型,要记住它们的区别在于,浏览器对象模型访问的是浏览器自身的信息和功能,通过操作window对象,可以访问多种不同的浏览器的功能。因为Window是根元素,所以它常常被隐式地使用。下面的两个JavaScript语句是等同的:
alert("隐式的访问window对象");
等同于
window.alert("显式的访问window对象");
window对象是默认首先访问的对象。因此,从window对象访问document对象也可以用相同的方式进行,例如访问document对象的write方法:
window.document.write("显式window对象");
和
document.write("隐式window对象");
由于web应用程序的开发会大量地访问浏览器对象模型,如history记录、页面中的框架信息等,Web开发人员很有必要了解更多的浏览器对象模型的细节。不过本书在这里不做更多的介绍,读者可以查阅MSDN以获取更多的帮组信息。
2.文档对象模型(DOM)
伴随着浏览器的发展DOM逐渐完善。早期的DOM,存在于Netscape 2/3和Internet Explorer 3。它们的创建是为了达到相似的目的,不过实现上却彼此有很大的不同;另外,它们都提供了一些方法来访问HTML文档的元素,但是不能访问所有的HTML元素。这给使用者带来了很多的不便,也使早期的开发人员不能使用这样的DOM给用户创建更动态的页面。
Netscape 4.0和Internet Explorer 4在DHTML基础上工作,开发人员可以通过DOM访问更多的HTML元素以及它们的属性,能够通过DOM创建真正的动态页面。不过由于浏览器提供商的不同,使Netscape和IE浏览器在DOM的实现上有各种各样的差别,给开发人员带来了许多痛苦。
Internet Exploer 5/5.5/6/7.0,Netscape 6+以及随后的Mozilla/Firefox浏览器开始按照W3C的推荐标准建造它们的对象模型,不过不能指望在短时间内,所有浏览器会为JavaScript开发人员提供完全一致的访问接口。
按照DOM的发展过程,W3C组织将DOM分为了4个版本。前3个版本(DOM 0、1、2)已经被各种不同的浏览器所实现;版本3刚成为W3C的建议标准,还没有浏览器实现它。下面介绍DOM的4个版本。
(1)DOM版本0。这个版本的DOM不是W3C所定义的,它和早期的浏览器中(Netsacape 2/3、Internet Explorer)中提供的DOM非常近似。这个版本的DOM更象是一系列功能的定义,提供了有限的HTML文档元素的访问。
(2)DOM版本1。这个版本的DOM定义了如何通过一系列公共的方法和属性来操作所有的文档元素。在这个版本中,DOM暴露所有的文档元素用以读写操作。DOM 1包含了两个元素—DOM Core和DOM HTML。
① DOM Core元素引用了一系列的可以代表任何结构化文档的通用接口,例如XML文档。DOM Core为其他的结构化文档提供了访问支持,如XML。
② DOM HTML元素引用了更高版本的接口以提供便捷的HTML文档的访问。DOM HTML元素在内部会引用DOM Core的接口。
主要的浏览器都能对这版本的DOM提供完善的支持。该版本在1998年成为W3C的推荐标准。
(3)DOM版本2。这个版本包括了版本0和1的所有功能,并进行了很大的扩充。它提供了操作层叠样式表元素的方法,还提供了更多的与XML相关的页面元素的访问方式。即使是宣称自己符合DOM版本2的浏览器也没有能完全支持DOM 2。DOM 2包含了6个方面的内容:DOM2 Core、DOM2 HTML、DOM2 Style/CSS、DOM2 Event、DOM2 Traversal and Range、DOM2 Views。
① DOM2 Core和DOM2 HTML是DOM版本1的DOM Core和DOM HTML的延伸。
② DOM2 Style/CSS提供了访问样式表元素的接口和方法。
③ DOM2 Event定义了一个通用的页面事件系统,它引入了事件流、事件抛出和取消等概念。
④ DOM2 Traversal and Range提供了方法允许脚本在文档的元素中自由的移动,以及识别出文档的一部分。
⑤ DOM2 Views允许脚本动态的访问和更新文档显示的内容。
⑥ DOM版本2在2000年被批准,但是到2003年1月才正式成为W3C的推荐 标准。
(4)DOM版本3。版本3代表了DOM最终的标准化方向。在这个版本中,将DOM对XML的支持扩展到1.1版本,以及其他与XML相关的特性的支持。在这个版本中,DOM符合了XML Schema 1.0和SOAP 1.2等W3C的XML推荐标准。DOM版本3增加了5个不同方面的内容:DOM3 Core、DOM3 Load and Save、DOM3 Validation、DOM3 Events和DOM3 Xpath。
① DOM3 Core是对DOM Core和DOM2 Core的进一步扩充。
② DOM3 Load and Save允许在DOM中动态的加载XML文档,同时规定了如何将DOM文档序列化为XML文档。
③ DOM3 Validation提供了动态更新文档的方法并保证文档和更新内容格式的合 法性。
④ DOM3 Events扩展了DOM2 Events中定义的事件系统,并对键盘事件的处理作了进一步的强化。
⑤ DOM3 XPath提供了以XPath语法访问DOM对象内部的HTML元素树的途径。
⑥ 这个版本的DOM还没有全部成为推荐标准。到目前为止,DOM3 Core、DOM3 Load and Save、DOM3 Validation成为了推荐标准。
可以预见的是,如果未来的浏览器符合DOM3的标准,将极大方便开发人员的工作。
接下来,看一个简单页面的DOM结构。下面是这个页面的HTML代码:
<!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>DOM 结构</title>
</head>
<body>
<h1>标题</h1>
<p> 这是一段文字</p>
<ul>
<li> Item 1</li>
<li>Item 2</li>
</ul>
</body>
</html>
这个页面对应的DOM结构如图15-4所示。
从图15-4可以非常直观地得出DOM结构是一棵N叉树,随着HTML标签的增加,树将迅速变得非常复杂。
图15-4 DOM结构示意图
15.2.3 CSS(层叠样式表)
层叠样式表(Cascading Style Sheets,简称CSS),是Web应用程序设计中非常重要的一个环节。不论是在传统的Web应用程序中还是在最近的AJAX程序中,CSS都被频繁地使用。样式表提供了一个页面视觉效果的中央控制系统。通过样式表可以精确控制每个独立的元素的外观和行为。除了每个元素单独的颜色、边框、背景、透明度和大小等属性可以在样式表中进行控制以外,样式表还可以控制一组元素的排列方式,元素在页面中的布局形式等。样式表还可以实现一些令人惊叹的视觉效果,如可以对元素应用某些滤镜效果。
在Web应用程序中,样式表可以存放在一个单独的文件中,并且不同Web页面可以使用相同的样式文件。修改一个样式表文件就可以改变所有引用该文件的页面的视觉效果。这与ASP.NET 2.0的母版页有类似之处,不过母版页着眼于页面的布局,而样式表关注的是每一类或是每一个元素的视觉效果。
样式表带给了Web应用程序非常多的好处,掌握样式表的使用是一个合格的Web页面设计人员的必修课。样式表可以为设计人员作以下的事情:
(1)将格式和结构分离。HTML定义了网页的结构和各个元素的功能,让浏览器自己决定如何显示这些HTML元素。最初的时候,HTML不包括<Font>、<I>等控制页面外观的标签;然后,HTML加入了这些标签,设计人员开始大量使用它们来控制页面外观,HTML变得异常的复杂和混乱。向一个现成的HTML文档中添加内容不再是一件轻松的事情。样式表的出现让HTML代码可以重新变得简洁。样式表定义元素的格式,HTML定义页面的结构,两者的功能分割开来,使开发人员能更加轻松地对页面的布局进行控制。
(2)更加精确地控制格式。HTML能够通过一些标签对格式进行一些控制,但是,不够精确,例如,HTML不能精确地生成80像素的高度,不可能控制行间距或者字间距,不能在页面上精确的定位图像的位置,而样式表可以。
(3)可以制作出体积更小下载更快的网页。使用样式表可以减少表格的标签及其他增加HTML体积的代码,减少为控制格式使用的空白图片的用量,由于多个页面可以共用一个样式表文件,只下载一次,则所有页面都可以使用它。
从HTML文档中加载样式表有以下4种方式。
(1)链接外部的样式表文件。
可以通过HTML的Link元素来指定外部的样式表,下面是几个使用Link元素加载样式表文件的实例:
<LINK REL=StyleSheet HREF="mystyle.css" TYPE="text/css" MEDIA=screen>
<LINK REL=StyleSheet HREF="mystyle.css" TYPE="text/css" MEDIA="screen,print">
<LINK REL=StyleSheet HREF="color.css" TYPE="text/css" TITLE="MyColorCSS" MEDIA="screen">
在LINK标签中,使用REL指定链接的文档与HTML文档之间的关系,这里指定链接的文档为样式表。使用TYPE属性指定链接文档的内容类型,“text/css”代表了层叠样式表文件;TITLE属性为引入的样式表指定一个名称。可以将多个外部样式表指定为同样的名称,这样它们就共同组成这个命名样式表,例如:
<LINK REL=StyleSheet HREF="1.css" TITLE="CSSTITLE">
<LINK REL=StyleSheet HREF="2.css" TITLE="CSSTITLE">
<LINK REL=StyleSheet HREF="3.css" TITLE="CSSTITLE">
上面的例子中,引入3个外部样式表文件,在HTML文档中被纳入名称为CSSTTITLE的样式表中。
(2)内嵌的样式表。
可以使用STYLE元素在文档中嵌入样式表:
<HTML>
<HEAD>
<STYLE TYPE="text/css" MEDIA="screen">
<!--
BODY {background:url(foo.gif) red;color:black}
P EM {background:yellow; color:black}
.note {margin-left:5em;margin-right: 5em}
-->
</STYLE>
</HEAD>
<BODY>
</BODY>
</HTML>
在STYLE标签中,同样可以指定TYPE属性和MEDIA属性。为了避免不支持样式表的浏览器将样式表代码显示给用户,需要把样式表代码放置在<!-- -->这个注解标签之内。
(3)输入样式表。
在STYLE标签中,除了直接将样式表代码列出之外,还可以使用@import指令输入一个外部的样式表文件,例如:
<STYLE TYPE="text/css" MEDIA="screen">
<!--
@import url(http://localhost/1.css);
@import url(/css/mysitestyle.css);
TD {background:green; color:lightblue}
-->
</STYLE>
(4)内联样式。
样式可以使用STYLE属性,在BODY(包括BODY)标签之内的标签声明中使用内联。样式表的代码放在style属性后面的文本中,样式表条目之间用分号分割,样式表条路数量不限,如下所示:
<table border="0" cellpadding="0" cellspacing="0" style="width: 100%; height: 50%">
<tr>
<td colspan="2" style="height: 200px">
</td>
</tr>
<tr>
<td style="width: 200px">
</td>
<td>
</td>
</tr>
</table>
上面就是4种使用样式表的方法。样式表的使用足够用一本书来详细解说,这里就不再花更多的笔墨来说明了,读者如果对样式表不是很了解,可以查阅相关的书籍和文档。
15.2.4 XMLHttpRequest对象
没有XMLHttpRequest对象就没有AJAX。这句话反映了XMLHttpRequest对象在AJAX应用程序中的重要性。为什么这样说呢?AJAX的核心思想是浏览器与Web服务器的异步通信,而与异步通信有关的操作全部通过XMLHttpRequest对象来完成。
XMLHttpRequest对象所使用的技术自1998年就开始使用了。微软大概是第一个使用这个技术的公司。微软在它的Outlook Web Access这个产品中首次使用了这个技术。通过使用相关的ActiveX组件,Internet Explorer 4.0浏览器中已经可以使用XMLHttpRequest对象了。
近两年,Google成功地将XMLHttpRequest技术用于它的产品中,如GoogleMap和GMail,因此在Web业界掀起了使用XMLHttpRequest技术的浪潮,随之而来的才是AJAX这个新的名称。
1.XMLHttpRequest对象的创建
其他的浏览器提供商在Internet Explorer支持XMLHttpRequest对象不久以后,意识到XMLHttpRequest的重要性,也各自在自己的产品中加入了对XMLHttpRequest的支持。到目前为止,所有的主流浏览器都支持XMLHttpRequest对象。兼容性的问题再次出现,非Internet Explorer核心的浏览器并不支持ActiveX组件的使用,它们将XMLHttpRequest对象内置在浏览器的内部,因此创建的方式有所不同。这个问题很容易解决,下面的代码演示了如何判断浏览器的种类并创建一个XMLHttpRequest对象:
var xmlHttpObj;
if (window.ActiveXObject)
{
try
{
xmlHttpObj = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e)
{
xmlHttpObj = new ActiveXObject("Msxml2.XMLHTTP");
}
}
else
xmlHttpObj = new XMLHttpRequest();
上面的代码中,window.ActiveXObject属性用来判断浏览器是否支持创建ActiveX组件对象,如果值为True,那么说明这个浏览器是Internet Explorer系列的浏览器,反之则不是。对于Internet Explorer系列的浏览器,使用new ActiveXObject(“Microsoft.XMLHttp”)来创建一个XMLHttpRequest的对象,否则使用new XMLHttpRequest()创建对象。由于Internet Explorer所在系统中,安装的XML Parser版本不同,new ActiveXObject()方法中指定的XMLHttpRequest对象的名称也略有区别。幸运的是,不论安装的是什么版本的XML Parser,都会包含“Microsoft.XMLHttp”或“Msxml2.XMLHTTP”。
毫无疑问,上面这段代码将在AJAX模式的应用程序中无数次使用。于是,把上面这段代码放在一个方法内:
function GetXmlHttpRequest()
{
var xmlHttp=null;
if (window.ActiveXObject)
{
try
{
xmlHttpObj = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e)
{
xmlHttpObj = new ActiveXObject("Msxml2.XMLHTTP");
}
}
else
xmlHttpObj = new XMLHttpRequest();
return xmlHttp;
}
以后再要使用XMLHTTPRequest对象,直接调用GetXmlHttpRequest方法即可。
2.向服务器发出请求
使用XMLHttpRequest对象向Web服务器发出请求非常简捷。只需要提供要请求的URL地址(要求请求的URL页面能返回结构化的数据,不论是HTML格式还是XML格式)。下面的方法中,示例了如何使用XMLHttpRequest对象发送请求:
function SyncXMLHttpCall()
{
var xmlHttpObj;
xmlHttpObj = GetXmlHttpRequest ();
if (xmlHttpObj)
{
xmlHttpObj.open("GET","http://" + location.host +
"//Data.xml", false);
xmlHttpObj.send(null);
alert("Request/Response Complete.");
}
}
上面的代码非常简单,但是它演示了使用XMLHttpRequest对象向服务器发送请求的基本流程。来看看它是如何做的:
(1)声明变量xmlHttpObj用来存放自GetXmlHttpRequest方法得到的XMLHttpRequest对象。
(2)在检查了xmlHttpObj不为空,即成功获取了XMLHttpRequest对象之后,使用XMLHttpRequest对象的open方法,传递参数“GET”、服务器页面的URL字符串和一个布尔值。
(3)调用send方法发送请求。
(4)上面的这段代码采用同步发送请求的方式,在调用send方法之后,浏览器必须等待send方法返回以后才能继续。这就和传统的请求响应方式没有什么区别。
那么如何使用异步的方式发送请求呢?先来解释一下XMLHttpRequest对象的open方法的3个参数的意义:
l 第1个参数指定了HTTP请求的方式,可以是“GET”、“POST”、“PUT”和“HEAD”中的一个,在AJAX程序中,用的最多的是“GET”。
l 第2个参数是要请求的服务器页面的URL。在这个例子中,是服务器上一个XML文档。
l 第3个参数是布尔值。当这个参数为false时,请求发送采用同步的方式,反之就使用异步的方式发送请求。
下面的代码演示如何使用异步模式向服务器发送请求。
var xmlHttpObj=null;
function OnReadyStateChange()
{
var ready=xmlHttpObj.readyState;
var data=null;
if(ready==READYSTATE_COMPLETE)
{
data=xmlHttpObj.responseText;
alert(data);
}
else
{
document.write("请等待…");
}
}
function asyncXMLHTTPCall()
{
xmlHttpObj = GetXmlHttpRequest ();
if (xmlHttpObj)
{
xmlHttpObj.open("GET","http:// " + location.host +
"/DataFile.xml", true);
xmlHttpObj.onreadystatechange =OnReadyStateChange;
xmlHttpObj.send(null);
}
}
在上面代码的asyncXMLHttpCall方法中,调用XMLHttpRequest对象的open方法,最后一个参数的值为true,表示了使用的是异步传送请求到服务器。注意xmlHttpObj.onreadystatechange=OnReadyStateChange;这个语句是用来注册一个回调方法来处理ReadyStateChange事件,这和C#的事件处理声明方法基本相同。当xmlHttpObj代表的请求的状态发生改变时,表示服务器有响应返回。当请求的状态改变,浏览器就会调用OnReadyStateChange方法来处理事件。在OnReadyStateChange方法中,首先检查请求的状态,查看其是否等于READYSTATE_COMPLETE,如果等于READYSTATE_COMPLETE则表示服务器正常响应了请求。请求可以有多个状态:
l READYSTATE_UNINITIALIZED—默认的初始化状态。
l READYSTATE_LOADING—对象正在载入它的属性。
l READYSTATE_LOADED—对象初始化完成。
l READYSTATE_INTERACTIVE—对象可以进行操作,但并非所有的数据都可用。
l READYSTATE_COMPLETE—对象接收完所有的数据。
如果检查发现请求已经完成,则把响应的信息用alert方法显示在窗口中,否则在页面中显示“请等待…”的字样。
上面的示例演示了如何使用XMLHttpRequest对象与Web服务器进行异步的通信。实例中的代码在每个AJAX程序中都必不可少。XMLHttpRequest对象除了open和send方法以外,还有其他的一些相关方法,表15-1所示的是一个完整的XMLHttpRequest方法列表。
表15-1 XMLHttpRequest的方法列表
abort | 取消当前的HTTP请求 |
getAllResponseHeaders | 返回服务器响应信息所有的头部信息 |
getResponseHeader | 返回服务器响应某一个头部信息 |
open | 初始化一个XMLHttpRequest请求对象,并指定请求的路径、请求发送的方式和同步/异步模式 |
send | 发送请求到Web服务器,并获取服务器的响应信息 |
setRequestHeader | 设定请求的头信息 |
下面对这些方法进行解释。
l open方法的原型是open(string method, string url, boolean asynch, string username, string password):这个方法初始化发向服务器的请求信息。它包含两个必需的参数和三个可选参数。使用这个方法必须提供服务器的URL和关于请求发送方式(一般为POST、GET和PUT三种之一)。可选参数中,第一个参数指定是否按异步模式进行请求,默认值是true;然后,如果服务器需要验证用户名和密码,那么还需要传递用户名和密码的参数。
l send(content)方法:这个方法执行实际的请求发送操作。如果请求的类型是异步的,那么send方法立刻返回,反之send方法会等待直到收到服务器的响应以后才返回。send方法有一个可选的参数,参数的类型可以是一个DOM对象,也可以是一个输入流或者是一个字符串,参数的内容将被作为请求的body的一部分传送给服务器。
l abort方法:按照字面意义理解即可。abort方法在收到服务器响应之前,立刻终止 请求。
l setRequestHeader(string header, string value)方法:方法为指定的Header元素指派一个值。这个方法必须在open方法调用之后使用。
l getAllResponseHeaders方法:获取响应中所有的Header信息。
l getResponseHeader(string header):方法获取响应中指定header的信息。
XMLHttpRequest对象除了这些方法,还有一些公共的属性可以使用,表15-2列出了所有可用的属性:
表15-2 可用的属性
onreadystatechange | 为readystate属性发生变化时,指定一个事件处理方法 |
readyState | 代表属性的状态。只读 |
responseBody | 服务器响应信息的形式之一 |
responseStream | 服务器响应信息的形式之一 |
responseText | 服务器响应的具体信息 |
responseXML | 服务器响应的具体信息。如果响应内容的content-type为xml,那么这个属性有值 |
status | HTTP状态值 |
statusText | HTTP状态文本 |
15.2.5 AJAX应用示例
在前面的几个小节介绍了AJAX的核心概念之后,在本节通过一个小型的AJAX应用让读者对AJAX有更进一步的理解。下面例子模仿互联网上众多站点中的注册模块的功能,并用AJAX来实现注册功能。
首先,用Visual Studio 2005新建一个空白站点,如图15-5所示。
图15-5 新建空白站点
在解决方案浏览器中,为站点添加一个HTML页面—signup.htm。简单地设计signup.htm页面,如图15-6所示。
图15-6 signup.htm页面设计图
signup.htm页面的HTML代码如下所示:
<!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>Untitled Page</title>
<script type="text/javascript" language="javascript" src="JScript.js">
</script>
</head>
<body style="font-size: 12pt" bgcolor="#ffffff">
<span style="color: #33cc33"><span style="font-family: 华文新魏">新用户注册:</span><br />
</span>
<div id="regdiv" style="width:401px;Height:149px;float:left;background-color: AliceBlue; text-align: center; border-right: #99cccc 1px solid; border-top: #99cccc 1px solid; border-left: #99cccc 1px solid; border-bottom: #99cccc 1px solid;">
<div style="width: 250px; height: 24px;float:left; text-align: left; border-top-width: 1px; border-bottom-width: 1px; border-bottom-color: black; border-top-color: black;" id="DIV1">
<span style="font-size: 10pt">用户名:</span><input id="username" name="username" style="width: 120px; left: 14px; position: relative; top: 2px; height: 14px;" type="text" /></div>
<div style="width: 150px; height: 24px;float:left; text-align: center; border-top-width: 1px; border-bottom-width: 1px; border-bottom-color: black; border-top-color: black;">
<input id="verifyusername" type="button" value="验证用户名" οnclick="userbtnClick();" style="border-right: silver thin solid; border-top: silver thin solid; border-left: silver thin solid; width: 79px; border-bottom: silver thin solid; height: 21px" /></div>
<div style="width: 250px; height: 24px;float:left; text-align: left;">
<span style="font-size: 10pt">密码<span style="font-size: 12pt">:</span></span><input id="password" style="width: 120px; font-size: 12pt; left: 24px; position: relative; top: 2px; height: 14px;"
type="password" name="password" /></div>
<div style="width: 150px; height: 24px;float:left; font-size: 12pt;">
</div>
<div style="width: 250px; height: 24px;float:left; text-align: left; font-size: 12pt;">
<span style="font-size: 10pt">重复密码:<input id="password2" style="width: 120px; position: relative; top: 2px; height: 14px;" type="password" name="password2" /></span></div>
<div style="width: 150px; height: 24px;float:left">
</div>
<div style="width: 250px; height: 24px;float:left; text-align: left;">
<span style="font-size: 10pt">电子邮件:<input id="emailtext" style="width: 120px" type="text" name="emailtext" /></span></div>
<div style="width: 150px; height: 24px;float:left; text-align: center;"><input id="verifyemailBtn" type="button" value="验证Email是否可用" οnclick="emailbtnClick();" style="border-right: silver thin solid; border-top: silver thin solid; border-left: silver thin solid; width: 135px; border-bottom: silver thin solid; height: 21px" /></div>
<div id="infodiv" style="width: 400px; height: 24px;float:left; text-align: left;"></div>
<input id="signupbtn" name="signupbtn" style="position: relative; top: 4px; height: 21px"
type="button" value="注册" οnclick="signupBtnClick();"/>
</div>
</body>
</html>
signup.htm页面主要使用DIV标签来建立表格,因此在页面的HTML代码中不会存在<TABLE>、<TR>和<TD>标签。DIV标签更适合与层叠样式表的配合使用。虽然本例中只使用了内联的样式,但是可以很方便地转换为外置样式表方式。在页面中,有三个按钮,分别执行检查邮件的是否合法、用户名是否合法以及注册功能。这三个按钮所引发的操作将通过AJAX来完成。
为了让服务器端能为浏览器的XMLHttpRequest对象的请求提供数据,因此这里先创建了一个Web Service,来为客户端提供数据。在站点的解决方案浏览器中添加adduser.asmx这个web service。adduser.asmx的代码如下所示:
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class adduser : System.Web.Services.WebService {
public adduser () {
//Uncomment the following line if using designed components
//InitializeComponent();
}
[WebMethod]
public bool verifyEmail(string email)
{
System.Threading.Thread.Sleep(2000);
if (email.Contains("@"))
return true;
else
return false;
}
[WebMethod]
public bool verifyUserName(string username)
{
System.Threading.Thread.Sleep(2000);
return true;
}
[WebMethod]
public bool createUser(string username,string password,string email)
{
System.Threading.Thread.Sleep(2000);
return true;
}
}
adduser..asmx的代码中包含了3个方法:vefifyEmail(string email)、verifyUserName(string username)和createUser(string username, string password, string email)。为了简便,没有执行具体的数据库操作来真正地添加用户,查找用户。这个示例主要是为了演示AJAX的使用。
有了Web Service作为服务器端的数据源,现在要做的就是实现AJAX的代码。在解决方案中添加一个JavaScript文件—Jscript.js。Jscript.js代码如下:
var http=GetXmlHttpRequestObject();
function GetXmlHttpRequestObject()
{
var xmlHttpObj=null;
if (window.ActiveXObject)
{
try
{
xmlHttpObj = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e)
{
xmlHttpObj = new ActiveXObject("Msxml2.XMLHTTP");
}
}
else
xmlHttpObj = new XMLHttpRequest();
return xmlHttpObj;
}
function CheckEmailAvailable(url,email,element)
{
var okmsg="<font style='font-weight:normal;color:green;position:relative; top:3px'>恭喜您,该邮件还没有被使用</font>";
var failmsg="<font style='font-weight:normal;color:black;position: relative;top:3px'>该邮件地址已经注册过,请尝试其他的</font>";
var waitmsg="请稍候... ...";
http.open("POST",url,true);
http.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
{
http.onreadystatechange=function()
{
if(http.readyState==4)
{
if(http.status==200)
{
var ret=http.ResponseXml.selectSingleNode("boolean/text()").text;
if(ret=='true')
{
document.getElementById(element).innerHTML=okmsg;
}
else
document.getElementById(element).innerHTML=failmsg+ret;
}
}
else
{
document.getElementById(element).innerHTML=waitmsg;
};
};
http.send("email="+email);
}
};
function emailbtnClick()
{
if(document.getElementById("emailtext").value=="")
{
alert("请填写邮件地址");
return;
}
var url="/AJAXDemo/adduser.asmx/verifyEmail";
var email=document.getElementById("emailtext").value;
CheckEmailAvailable(url,email,"infodiv");
}
function CheckUserNameAvailable(url,username,element)
{
var okmsg="<font style='font-size:10px;font-weight:normal;color:green; position:relative;top:3px'>恭喜您,该用户名还没有被使用</font>";
var failmsg="<font style='font-size:10px;font-weight:normal;color:black; position:relative;top:3px'>该用户名已经注册过,请尝试其他的</font>";
var waitmsg="请稍候... ...";
http.open("POST",url,true);
http.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
{
http.onreadystatechange=function()
{
if(http.readyState==4)
{
if(http.status==200)
{
var ret=http.ResponseXml.selectSingleNode("boolean/text()").text;
if(ret=='true')
{
document.getElementById(element).innerHTML=okmsg;
}
else
document.getElementById(element).innerHTML=failmsg+ret;
}
}
else
{
document.getElementById(element).innerHTML=waitmsg;
};
};
http.send("username="+username);
}
};
function userbtnClick()
{
if(document.getElementById("username").value=="")
{
alert("请填写用户名");
return;
}
var url="/AJAXDemo/adduser.asmx/verifyUserName";
var username=document.getElementById("username").value;
CheckUserNameAvailable(url,username,"infodiv");
}
function signupNewUser(url,element,username,password,email)
{
var failmsg="<font style='font-size:10px;font-weight:normal;color: black;position:relative;top:3px'>注册失败</font>";
var waitmsg="请稍候... ...";
http.open("POST",url,true);
http.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
{
http.onreadystatechange=function()
{
if(http.readyState==4)
{
if(http.status==200)
{
var ret=http.ResponseXml.selectSingleNode("boolean/text()").text;
if(ret=='true')
{
document.getElementById("regdiv").visible=false;
document.getElementById("regdiv").removeNode(true);
var divelement=document.createElement("DIV");
divelement.style.cssText="width:200px;height:100px;background-color:#ccff66;text-align:center";
divelement.innerHTML="<span style='color:blue;position:relative;top:35px'>注册成功!</span>";
document.body.appendChild(divelement);
}
else
document.getElementById(element).innerHTML=failmsg;
}
}
else
{
document.getElementById(element).innerHTML=waitmsg;
}
}
http.send("username="+username+"&password="+password+"&email="+email);
}
};
function signupBtnClick()
{
var url="/AJAXDemo/adduser.asmx/createUser";
var username=document.getElementById("username").value;
var password=document.getElementById("password").value;
var password2=document.getElementById("password2").value;
var email=document.getElementById("emailtext").value;
var element="infodiv";
if(password!=password2)
{
alert("密码不一致");
return;
}
if(username==null)
{
alert("请填写用户名");
return;
}
if(email==null)
{
alert("请填写电子邮件");
return;
}
signupNewUser(url,element,username,password,email);
};
上面的代码保存在附赠光盘的“第15章/AJAXDemo/jscript.js”文件中。
上面就是Jscript.js文件中所有的代码。下面对代码中的方法进行解释:
(1)GetXmlHttpRequestObject()方法:该方法返回一个XMLHttpRequest对象。在本例中,使用全局变量http来保存这个XMLHttpRequest对象。GetXmlHttpRequestObject方法使用了之前讲到的XMLHttpRequest对象的创建方法。
(2)CheckEmailAvailable(url,email,element)方法。该方法向参数url指定的地址发出请求来验证由email参数指定的邮件地址。在这个方法中,按照XMLHttpRequest对象发送异步请求的步骤向服务器发送请求。首先,在方法中调用了XMLHttpRequest对象的open方法初始化请求;接着设定请求的Header信息;然后,为readystatechange事件注册事件处理方法。这里使用了Javascript的内部方法,与C# 2.0中的匿名方法非常类似。在匿名方法的内部,检查XMLHttpRequest的状态,如果不是处于完成状态,则使用CheckEmailAvailable方法的element参数传入的HTML元素,在这个HTML标签之内显示“请等待”的信息。当readystate为完成状态以后,通过XMLHttpRequest对象的ResponseXML属性获取响应信息。通过响应信息的结果,来判断是否当前输入的邮件地址可用。如果可用,显示恭喜的信息,反之显示红色的信息通知用户,邮件地址不可用。
图15-7所示是请求验证时页面显示的信息。
图15-8所示的是邮件地址不可用时所显示的信息。
图15-9所示是邮件地址顺利通过验证的情景。
图15-7 等待信息
图15-8 邮件地址有问题 图15-9 邮件地址通过验证
(3)function emailbtnClick()方法:该方法处理“验证emai是否可用”按钮的点击事件。在该方法内部,以email的输入值和服务器上对应的url作为参数,调用CheckEmailAvailable方法。
(4)function CheckUserNameAvailable(url,username,element)方法:该方法的工作原理与CheckEmailAvailable方法相同,也是通过XMLHttpRequest对象的异步请求与服务器通信验证用户名。
(5)function signupNewUser(url,element,username,password,email)方法:该方法仍然是通过XMLHttpRequest来异步的请求服务器,它把用户名、密码和邮件地址作为请求的一部分传递给服务器的Web Service。如果注册成功Web Service会返回一个“true”的结果。成功以后,代码删除注册模块所在的DIV标签,加入新的DIV标签用来显示注册成功的信息。
这个例子演示一个最基本的AJAX应用,仅仅是简单的通过XMLHttpRequest发送请求,并接受响应的结果,再通过Javascript来操作DOM显示结果。复杂的应用可能还涉及到异步通信队列的问题。由于,每个页面同一时刻只能有两个请求通过XMLHttpRequest对象发出,如果页面中包含大量的异步请求需要发送,那么还需要人工实现请求的排队。