白桦的天空

第一次的心动,永远的心痛!
  首页  :: 新随笔  :: 联系 :: 管理

创建跨平台的Ajax应用

Posted on 2005-05-31 22:35  白桦的天空  阅读(462)  评论(0编辑  收藏  举报

何为Ajax?

Ajax不是什么新技术,是Asynchronous JavaScript and XML的简称. 它是javascript和xml等技术应用的结合。具体包括:

  • DHTML和CSS
  • 使用文档对象模型(Document Object Model)作动态显示和交互
  • 使用XML和XSLT做数据交互和操作
  • 使用XMLHttpRequest进行异步数据接收
  • 使用JavaScript将它们绑定在一起

有了Ajax技术,我们可以在一定程度上实现Rich Internet Client界面。我们来看一个例子。要设计一个Web界面,用户从下拉式列表中选择一个省份,在另一个清单式列表中显示该省份的城市清单。而这些省份所拥有的城市数据是储存在服务器端数据库中的。在没有Ajax之前,用户在浏览器上每选择一次省份,则发生一次表单提交动作,将所选省份通过表单的POST请求发送到Servlet/jsp,然后服务器又返回另一个Web页面,里面包含该省份的数据。而用户在浏览器中所看到的就是页面被重新刷新了一次,会有明显的界面迟滞效果。

在Ajax技术出现之后,这一切都成为过去。用户看到的几乎是即时刷新的Web界面。后面我们会详细讨论如何在不同浏览器平台上实现这个例子,以及不同浏览器上编写脚本所就注意的事项。

XMLHttpRequest---Ajax的灵魂

要在浏览器上实现Ajax应用,需要创建一个XMLHttpRequest脚本对象,我们就是通过此对象来让javascript与服务器进行后台的异步交互,而交互的媒介就是XML.

XMLHttpRequest并不是一个W3C规定的标准技术[1], 所以在不同的浏览器上,创建XMLHttpRequest对象的方式也不一样.

1)	var xmlHttp;
2)	if (window.XMLHttpRequest) 
3)	{
4)		// 创建 Mozilla/FireFox平台的 XMLHttpRequest 对象
5)		xmlHttp = new XMLHttpRequest();
6)	} else if (window.ActiveXObject) 
7)	{
8)		// 创建 IE/Windows 平台的XMLHttp对象
9)		xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
10)	}

以上代码就创建了XMLHttpRequest脚本对象. 其中第5行是FireFox平台上的创建方式, 我只在FireFox平台上测试过, 由于Firefox是采用Mozilla为内核,理论上也可以在Mozilla上运行.第9行是IE上的创建方式.

所幸的是, 虽然创建方式不一样,但这两种对象所提供的方法和属性几乎是一模一样的,
常用的方法有:

    open("method","URL",async)
        "method"是数据提交的方式,一般有"POST","GET"等.
        "URL"是要开启的地址.
        async是指定是否用异步的方式处理请求. 

    send(content)
        'content'是要发送的数据内容.

    setRequestHeader("label","value")
        设置请求头部信息,例如指定content type等等.

常用的属性有:

    readyState
        返回XMLHttpRequest对象的状态.

    onreadystatechange 
        这是一个对象状态发生变化的事件处理器. 你可以为此属性指定一个处理函数, 用事件驱动的方式来处理XMLHttpRequest返回的数据. 通常XMLHttpRequest对象采用异步的方式开启URL时使用此属性. 

    responseXML
        以XML文档对象方式返回服务器的响应数据.

    responseText
        以文本方式返回服务器的响应数据.

XML DOM---沟通浏览器与服务器的桥梁

不管怎样, @3C为我们制定了XML DOM的规范, 而Mozilla或者Microsoft等浏览器厂商都承诺会在它们的浏览器中实现这个规范, 所以在用javascript解析xml dom时, 只要是用标准的dom对象, 基本上是可以跨平台运行的.

而对于DHTML的文档对象模型, 可就不那么幸运了, 例如我们要引用html页面中的一个表单元素,

    <html>
        <body>
            <input name="abc" id="abc" type="text" value="hello"/>
        </body>
    </html>

在IE上,可以用以下的脚本来显示这个字段中的值:

    alert(document.abc.text);

而在Mozilla/Firefox上就没那么好采了. 要用以下稍微麻烦一些的方式:

    alert(document.getElementById("abc").text);

而Mozilla的这种方式正是W3C所规定的标准. 而IE也没有食言,这种语法在IE 6.0上也工作正常!

Ajax的无刷新Web应用

回到我们前面提到的案例, 我们用Ajax来做一个各省城市查询页面.

ajax1.jpg

为了方便演示,我们的客户端页面采用htm格式, 服务器端采用jsp来处理请求.

在客户端页面,我们主要编写了2个javascript函数, 一个用来创建XMLHttpRequest请求和发送请求数据.另一个用来处理返回的xml文档数据.

	// 从指定URL加载XML文档数据对象。
	// @param url 请求地址
	// @param reqText 请求数据
	function loadXMLDoc(url,reqData) 
	{
		try
		{
			var xmlHttp;
		   if (window.XMLHttpRequest) 
		   {
			   // 创建 Mozilla/FireFox平台的 XMLHttpRequest 对象
		   		xmlHttp = new XMLHttpRequest();
		   } else if (window.ActiveXObject) 
		   {
			   // 创建 IE/Windows 平台的XMLHttp对象
				xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
			}
	       // 用POST,非异步方式开启请求地址,
	       xmlHttp.open("POST", url, false);
	       // 设置提交数据的格式为Form表单格式。
	       // 如要发送xml格式的数据,将此行注释掉即可。
	       // 或者显式指定为"Content-Type", "text/xml; charset=utf-8"
	       xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
	       // 发送数据
	       xmlHttp.send(reqData);
			// 返回xml文档数据对象
			return xmlHttp.responseXML;
		}catch(exception)
		{
			//alert(exception);
		}
	}

	// 处理XML文档数据
	function processData(param)
	{
		if ( param==null ) return;
		// 请求参数
		var sParam = "country="+param;
		// 请求地址
		var sUrl = "http://turbo:8080/qis/Ajax.jsp";
		// 加载xml文档数据对象
		var xmlDOM = loadXMLDoc(sUrl,sParam);
		// 下面开始将返回的xml数据解析出来,并添加到列表中。
		var root = xmlDOM.documentElement;
		try
		{
			document.getElementById("city").length = 0;
			for (i=0;i<root.childNodes.length;i++)
			{
				sValue = root.getElementsByTagName('city')[i].firstChild.data;
				if(sValue!=null)
				{
					//将解析出来的城市名添加到清单列表中
					document.getElementById('city').length =
						document.getElementById('city').length+1;
					document.getElementById('city').options[i].text=sValue;
					document.getElementById('city').options[i].value=sValue;
				}
			}
		}catch(exception)
		{
			//alert(exception);
		}
	}

其中第1个函数用来从指定URL加载XML文档数据对象, 返回一个XML DOM对象, 你可以用标准的dom对象方法来解析其中的数据. 这个方法几乎是通用的, 你也可以将它复制到你的Ajax的页面中使用. 而第2个函数处理返回的DOM数据,并将数据添加到指定的表单元素中显示. 注意, 为了跨浏览器使用, 我们的代码里引用表单元素的方法是document.getElementById. 如果你要将它移植到你的程序中,你得重写xml数据解析的代码.

在表单元素选择省份的下拉式清单上, 我们加了onchange事件, 将当前所选省份传给processData函数处理,

    onchange="javascript:processData(this.value)

当选择省份时, 则马上调用processData函数, 从服务器端取数据.

而对于服务端程序, 为了部署简便, 我采用jsp来编写, 它主要负责处理用户请求, 并返回相应的xml文档数据, 其大致结构如下:

	// 创建文档对象。
	Document doc = DocumentBuilderFactory.newInstance()
    					.newDocumentBuilder().newDocument();
	// 创建文档对象根元素。
	Element xmlRoot = doc.createElement("result");
	
	String country = request.getParameter("country");
	// 如果是广东省,创建广东省城市数据文档树。
	if ( "GuangDong".equals(country) )
	{
	...
	}
	// 如果是湖北省,创建湖北省城市数据文档树。
	else if ( "HuBei".equals(country) )
	{
	...
	}
	// 如果是湖南省,创建湖南省城市数据文档树。
	else if ( "HuNan".equals(country) )
	{
	...
	}

	// 将文档根元素加到文档对象。
	doc.appendChild(xmlRoot);
	// 将xml文档数据发送给客户端浏览器。
	DOMSource doms = new DOMSource(doc);
	StreamResult sr = new StreamResult(response.getOutputStream( ));
	TransformerFactory tf = TransformerFactory.newInstance( );
	Transformer t = tf.newTransformer( );
	t.setOutputProperty("encoding", "UTF-8");
	t.transform(doms, sr);

你需要针对你的实际应用来写这个处理用户请求的服务程序.其中创建xml文档的过程可以从你的Java Bean, 或者Hibernate的JDO对象等. 总之, 你可以将任何数据创建成xml文档对象.

演示源码下载

Ajax.htm.txt(info) 这是演示程序的客户端Web页面.

Ajax.jsp.txt(info) 这是服务器端处理用户请求的程序. 为了部署方便,我们它写成一个jsp格式.

将这2个程序下载后, 将后面的扩展名.txt去掉, 然后复制到你的Tomcat的webapps相应的目录下, 例如ROOT目录, 然后就可以通过下面的地址访问了:
http://localhost:8080/Ajax.htm

本演示程序在Firefox 1.x和IE 6.0上测试通过.