通过阅读本章, 你可以:
-
了解 Ajax 开发模式与传统开发模式的比较
-
掌握如何使用 XMLHttpRequest 对象
-
通过 Ajax 向服务器发送请求
-
通过 Ajax 处理服务器的响应
-
通过 Ajax 实现检测用户名是否唯一
-
进行 Ajax 重构
-
通过 Ajax 实现实时显示公告信息
-
通过 Ajax 实现无刷新的级联下拉列表
-
通过 Ajax 实现上传文件时显示进度条
13.1 当下谁在用 Ajax
13.1.1 百度搜索提示
13.1.2 淘宝新会员免费注册
13.1.3 明日科技编程词典服务网
13.2 Ajax 开发模式与传统开发模式的比较
对于每个用户的请求, 在传统的 Web应用 模式中, 将生成一次HTTP请求, 而在 Ajax 应用 开发模式中, 将变成对 Ajax 引擎的一次 JavaScript 调用. 在 Ajax 应用开发模式中 通过 JavaScript 实现在不刷新整个页面的情况下, 对部分数据进行更新, 从而降低了网络流量, 给用户带来更好的体验.
13.3 Ajax 使用的技术
Ajax (Asynchronous JavaScript and XML) 是 XMLHttpRequest 对象 和 JavaScript, XML, CSS, DOM 等多种技术的组合. 其中, 只有 XMLHttpRequest 对象是新技术, 其他的均为已有技术.
XMLHttpRequest 对象
它是一个具有应用程序接口的 Javascript 对象, 能够使用 超文本传输协议(HTTP) 连接服务器, 是微软公司为了满足开发者的需要, 与1999年在 IE5.0 浏览器中率先推出的.
XML
XML 是 eXtensible Markup Language (可扩展的标记语言) 的缩写, 它提供了用于描述结构化数据的格式, 适用于不同应用程序间的数据交换, 而且这种交换不以预先定义的一组数据结构为前提, 增强了可扩展性. XMLHttpRequest 对象与服务器交换的数据通常采用XML格式.
[例13.1] (略)
注意: 在XML文档中, 必须有一个根元素, 所有其他的元素必须嵌入到根元素中.
JavaScript
Ajax 就是利用 JavaScript 将 DOM, XHTML (或 HTML), XML 以及 CSS 等技术综合起来, 并控制它们的行为的. 因为要开发一个复杂高效的 Ajax 应用程序, 就必须对 JavaScript 有深入的了解.
CSS
CSS 是 Cascading Style Sheet ( 层叠样式表) 的缩写, 用于(增强) 控制网页样式并允许将样式信息与网页内容分离的一种标记性语言.
DOM
DOM 是文档对象模型的简称, 是表示文本(如HTML文档)和访问, 操作构成文档的各种元素(如 HTML标记和文本串) 的应用程序接口. W3C 定义了 标准的 文档对象模型, 它以树形结构表示 HTML 和 XML 文档, 并且定义了 遍历树 和 添加, 修改, 查找树的节点的方法和属性. 在 Ajax 应用中, 通过 JavaScript 操作 DOM, 可以达到在不刷新页面的情况下实时修改用户界面的目的.
13.4 使用 XMLHttpRequest 对象
13.4.1 初始化 XMLHttpRequest 对象
IE 浏览器
IE 浏览器把 XMLHttpRequest 实例化为一个 ActiveX 对象. 具体方法如下:
|
1
2
3
|
var http_request = new ActiveXObject("Msxml2.XMLHTTP");
// orvar http_request = new ActiveXObject("Microsoft.XMLHTTP");
|
在上面的语法中, Msxml2.XMLHTTP 和 Microsoft.XMLHTTP 是针对 IE 浏览器 的不同版本而进行设置的, 目前比较常用的是这两种.
非IE浏览器
非IE浏览器(如 Chrome, FireFox, Opera, Mozzila, Safari) 把 XMLHttpRequest 对象实例化为一个本地 JavaScript 对象. 具体方法如下:
|
1
|
var http_request = new XMLHttpRequest();
|
为了提高程序的兼容性, 可以创建一个跨浏览器的 XMLHttpRequest 对象.
|
1
2
3
4
5
6
7
8
9
10
11
|
if(window.XMLHttpRequest){ // non-IE browser
http_request = new XMLHttpRequest();
} else if(window.ActiveXObject){ // IE browser
try{
http_request = new ActiveXObject("Msxml2.XMLHTTP");
} catch(e){
try{
http_request = new ActiveXObject("Microsoft.XMLHTTP");
}catch(e){}
}
} |
在上面的代码中, 调用 window.ActiveXObject 将返回一个对象, 或是 null. 在 if 语句中, 会把返回值看作是 true 或 false.
13.4.2 XMLHttpRequest 对象的常用方法
open方法
open 方法用于设置进行异步请求目标的 URL, 请求方法以及其他参数. 其具体语法如下:
open("method", "URL", [,asyncFlag,[,"username"[,"password"]]])
参数说明:
method: 指定请求的类型, 一般为 GET 或 POST.
URL: 指定请求地址, 可以是绝对地址或相对地址, 并且可以传递查询字符串.
asyncFlag: 为可选参数, 异步请求为 true, 同步请求为 false, 默认情况下为 true.
username: 为可选参数, 用于指定请求用户名, 没有时可省略.
password: 为可选参数, 用户指定请求密码, 没有时可省略.
[例13.2] 设置异步请求目标为 register.jsp, 请求方法为 GET, 请求方式为异步的代码如下:
|
1
|
http_request.open("GET", "register.jsp", true);
|
send(content) 方法
send() 方法用于向服务器发送请求. 如果请求声明为异步, 该方法立即返回, 否则将等到接受到响应为止.
content: 用于指定发送的数据, 可以是 DOM 对象的实例, 输入流 或 字符串. 如果没有参数需要传递, 可以设置为null.
[例13.3] 向服务器发送一个不包含任何参数的请求, 可以使用下面的代码:
|
1
|
http_request.send(null);
|
setRequestHeader()方法, 用于为请求的 HTTP头 设置值.
setRequestHeader("header", "value");
header: 用于指定 HTTP头
value: 用于为指定的 HTTP头 设置值.
注意: setReqeustHeader() 方法必须在调用 open() 方法之后才能调用.
[例13.4] 在发送POST请求时, 需要设置 Content-Type 请求头的值为 "application/x-www-form-urlencoded", 这是就可以通过 setRequestHeader() 方法进行设置. 具体代码如下:
|
1
|
http_request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
abort() 方法用于停止或放弃当前异步请求. 其语法格式如下:
abort()
getResponseHeader()方法, 用于以字符串形式返回指定的 HTTP头 信息. 其语法格式如下:
getResponseHeader("headerLabel")
参数说明:
headerLabel: 用于指定 HTTP头, 包括 Server, Content-Type 和 Date 等.
[例13.5] 要获取 HTTP头 Content-Type 的值, 可以使用以下代码:
|
1
|
http_request.getResponseHeader("Content-Type");
|
上面的代码将获取到类似以下内容:
text/html;charset=GB18030
getAllResponseHeaders() 方法, 用于以字符串形式返回完整的 HTTP头 信息, 其中包括 Server, Date, Content-Type 和 Content-Length.
[例13.6] 使用下面的代码调用 getAllResponseHeaders() 方法, 将弹出如图13.6 所示的对话框(省略)显示完整的 HTTP 头信息.
13.4.3 XMLHttpRequest 对象的常用属性
onreadystatechange 属性
onreadystatechange 属性用于指定状态改变时所触发的事件处理器. 在 Ajax 中, 每个状态改变时都会触发这个事件处理器, 通常会调用一个JavaScript 函数.
[例13.7] 指定状态改变时触发 JavaScript 函数 getResult 的代码如下:
|
1
|
http_request.onreadystatechange = getResult; |
注意: 在指定所触发的事件处理器时, 所调用的 JavaScript 函数不能添加小括号以及指定参数名. 不过这里可以使用匿名函数. 例如, 要调用带参数的函数 getResult(), 可以使用下面的代码:
|
1
2
3
|
http_request.onreadystatechange = function(){
getResult("添加的参数"); // 调用带参数的函数
}; // 通过匿名函数来指定要带参数的函数
|
readyState 属性
用于获取请求的状态
表 13.1 readyState 属性的属性值及其意义
| 值 | 意义 | 值 | 意义 |
| 0 | 未初始化 | 3 | 交互中 |
| 1 | 正在加载 | 4 | 完成 |
| 2 | 已加载 |
responseText 属性:
用于获取服务器的响应, 表示为支付串.
responseXML 属性:
responseXML 属性用于获取服务器的响应, 表示为 XML. 这个对象可以解析为一个 DOM 对象.
status 属性:
用于返回服务器的 HTTP 状态码, 常用的状态码 如表13.2 所示.
表13.2 status 属性的状态码
| 值 | 意义 | 值 | 意义 |
| 200 | 表示成功 | 404 |
文件未找到 |
| 202 | 表示请求被接受, 但尚未成功 | 500 | 内部服务器错误 |
| 400 | 错误的请求 |
statusText 属性
statusText 属性用于返回 HTTP 状态码对应的文本, 如 OK 或 Not Found 等.
13.5 与服务器通信--发送请求与处理响应
13.5.1 发送请求
无论发送GET请求还是POST请求, 都需要经过以下 4 个步骤:
(1) 初始化 XMLHttpRequest 对象. 为了提高程序的兼容性, 需要创建一个跨浏览器的 XMLHttpRequst 对象, 并且判断 XMLHttpRequest 对象的实例是否创建成功, 如果不成功, 则给予提示.
[例13.8] 发送请求.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
http_request = false;
if(window.XMLHttpRequest){ // non-IE browser
http_request = new XMLHttpRequest();
}else if(window.ActiveXObject){ // IE browser
try{
http_request = new ActiveXObject("Msxml2.XMLHTTP");
}catch(e){
try{
http_request = new ActiveXObject("Microsoft.XMLHTTP");
}catch(e){}
}
}if(!http_request){
alert("无法创建 XMLHttpRequest 对象实例!");
return false;
} |
(2) 为XMLHttpRequest 对象指定一个返回结果处理函数(即 回调函数), 用于对返回结果进行处理. 具体代码如下:
[例13.9] 设置回调函数.
|
1
|
http_request.onreadystatechange = getResult; //调用返回结果处理函数
|
注意: 使用 XMLHttpRequest 对象的 onreadystatechange 属性指定回调函数时, 不能指定要传递的参数.如果要指定传递的参数, 可以使用以下方法:
|
1
|
http_request.onreadystatechange = function(){ getResult(param) };
|
(3) 创建一个到服务器的连接. 在创建时, 需要指定发送请求的方式(即GET或POST), 以及设置是否采用异步方式发送请求.
[例13.10] 采用异步方式发送 GET 请求 的具体代码如下:
http_request.open('GET', url, true);
[例13.11] 采用异步方式发送 PSOT 请求的具体代码如下:
http_request.open('POST', url, true);
说明: 在 open() 方法中的 url 参数, 可以是一个 JSP页面的 URL 地址, 也可以是 Servlet 的映射地址.
技巧: 在指定 URL 参数时, 最好将一个时间戳追加到该 URL 参数的后面, 这样可以防止因浏览器缓存结果而不能实时得到最新的结果. 例如, 可以指定 URL参数 为以下代码:
|
1
|
String url="deal.jsp?nocache="+new Date().getTime();
|
(4) 向服务器发送请求. XMLHttpRequest 对象的 send() 方法 可以实现向 服务器发送请求, 该方法需要传递一个参数, 如果发送的是 GET 请求, 可以将该参数设置为null; 如果发送的是 POST 请求, 可以通过该参数指定要发送的 请求参数.
向服务器发送 GET 请求的代码如下:
|
1
|
http_request.send(null); // 向服务器发送 GET 请求
|
[例13.12] 向服务器发送POST请求的代码如下:
|
1
2
3
4
|
//需要注意的是, 在发送POST请求前,还需要设置正确的请求头http_request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
var param="user"+form1.user.value+"&pwd="+form1.pwd.value+&email="+form1.email.value; // 组合参数
http_request.send(param); // 向服务器发送请求
|
13.5.2 处理服务器响应
-
处理字符串响应
[例13.13] 将字符串响应显示到提示对话框中的回调函数的具体代码如下:
123456789functiongetResult(){if(http_request.readyState == 4){//判断请求状态if(http_request.status == 200){//判断响应状态alert(http_request.responseText);else{alert("您所请求的页面有错误!");}}}如果需要将响应结果显示到页面的指定位置, 那么可以预先在页面的适当位置添加一个<div>或<span>标记, 并设置其id属性, 然后在回调函数中使用如下代码显示响应结果:
1document.getElementById("div_result").innerHTML=http_request.responseText; -
处理XML响应
[例13.14] 保存图书信息的 XML 文档. 具体代码如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<?xml version="1.0" encoding="UTF-8"?>
<mr> <books>
<book>
<title>Java Web 程序开发范例宝典</title>
<publisher>人们邮电出版社</publisher>
</book>
<book>
<title>Java 范例完全自学手册</title>
<publisher>人们邮电出版社</publisher>
</book>
</books>
<mr> |
在回调函数中遍历图书信息的XML文档, 并将其显示到页面中的代码如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
function getResult{
if(http_request.readyState == 4) { //判断请求状态
if(http_request.status == 200){ //判断响应状态
var xmldoc = http_request.responseXML;
var str="";
for(i=0;i<xmldoc.getElementsByTagName("book").length;i++){
var book=xmldoc.getElementsByTagName("book").item(i);
str=str+" <<"+book.getElementsByTagName("title")[0].firstChild.data+">> 由 "+book.getElementsByTagName("publisher")[0].firstChild.data+" 出版<br>";
}
document.getElementById("book").innerHTML=str;
}else{
alert("您所请求的页面有错误!");
}
}
}<div id="book"></div>
|
13.5.3 一个完整的实例 -- 检测用户名是否唯一
[例13.15]检测用户名是否唯一. (实例位置: disc\TM\sl\13\1)
(1)创建index.jsp文件, 在该文件中添加用于收集用户注册信息的表单及表单元素, 以及代表"检测用户名"按钮的图片, 并在该图片的onclick事件中调用 checkName() 函数, 检测用户名是否已被注册.
|
1
2
3
4
5
6
7
8
9
|
<form method="post" action="" name="form1">
用户名:<input name="username" type="text" id="username" size="32"></td>
<img src="images/checkBt.jpg" width="104" height="23" style="cursor:hand;" onClick="checkUser(form1.username);"></td>
密码:<input name="pwd1" type="password" id="pwd1" size="35">
确认密码:<input name="pwd2" type="password" id="pwd2" size="35">
E-mail:<input name="email" type="text" id="email" size="45">
<input type="image" name="imageField" src="images/registerBt.jpg">
</form>
|
(2) 在页面的适当位置添加用于显示提示信息的<div>标记, 并通过CSS设置该<div>标记的样式.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<style type="text/css">
<!--#toolTip { position:absolute; //设置为绝对路径
left:331px; //设置左边距
top:39px; //设置顶边距
width:98px; //设置宽度
height:48px; //设置高度
padding-top:45px; //设置文字与顶边的距离
padding-left:25px; //设置文字与左边的距离
padding-right:25px; //设置文字与右边的距离
z-index:1;
display:none; //设置默认不显示
color:red; //设置文字的颜色
background-image: url(images/tooltip.jpg);//设置背景图片
}--></style><div id="toolTip"></div>
|
(3) 编写JavaScript函数 createRequest().
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
function createRequest(url) {
http_request = false;
if (window.XMLHttpRequest) { // 非IE浏览器
http_request = new XMLHttpRequest(); //创建XMLHttpRequest对象
} else if (window.ActiveXObject) { // IE浏览器
try {
http_request = new ActiveXObject("Msxml2.XMLHTTP"); //创建XMLHttpRequest对象
} catch (e) {
try {
http_request = new ActiveXObject("Microsoft.XMLHTTP"); //创建XMLHttpRequest对象
} catch (e) {}
}
}
if (!http_request) {
alert("不能创建XMLHttpRequest对象实例!");
return false;
}
http_request.onreadystatechange = getResult; //调用返回结果处理函数
http_request.open('GET', url, true); //创建与服务器的连接
http_request.send(null); //向服务器发送请求
} |
(4) 编写回调函数 getResult().
|
1
2
3
4
5
6
7
8
9
10
|
function getResult() {
if (http_request.readyState == 4) { // 判断请求状态
if (http_request.status == 200) { // 请求成功,开始处理返回结果
document.getElementById("toolTip").innerHTML=http_request.responseText; //设置提示内容
document.getElementById("toolTip").style.display="block"; //显示提示框
} else { // 请求页面有错误
alert("您所请求的页面有错误!");
}
}
} |
(5) 编写JavaScript函数 checkUser(), 用于检测用户名是否为空, 当用户名不为空时, 调用 createRequest() 函数发送异步请求检测用户名是否已被注册.
|
1
2
3
4
5
6
7
|
function checkUser(userName){
if(userName.value==""){
alert("请输入用户名!");userName.focus();return;
}else{
createRequest('checkUser.jsp?user='+userName.value);
}
} |
(6) 编写检测用户名是否已被注册的处理页checkUser.jsp.
|
1
2
3
4
5
6
7
8
9
10
11
12
|
<%@ page language="java" import="java.util.*" pageEncoding="GB18030" %>
<% String[] userList={"明日科技","mr","mrsoft","wgh"}; //创建一个一维数组
String user=new String(request.getParameter("user").getBytes("ISO-8859-1"),"GB18030"); //获取用户名
Arrays.sort(userList); //对数组排序
int result=Arrays.binarySearch(userList,user); //搜索数组
if(result>-1){
out.println("很抱歉,该用户名已经被注册!"); //输出检测结果
}else{
out.println("恭喜您,该用户名没有被注册!"); //输出检测结果
}
%> |
说明: 由于本实例比较简单, 这里没有从数据库中获取用户列表, 而是将用户列表保存在一个一维数组中. 在实际项目开发时, 通常情况下是从数据库中获取用户信息.
图13.7 检测用户名
13.6 解决中文乱码问题
Ajax 不支持多种字符集, 其默认的字符集是 UTF-8, 所以在使用 Ajax 技术的程序中, 应及时进行编码转换, 否则程序中出现的中文字符将变成乱码.
13.6.1 发送请求时出现中文乱码
(1) 当接收使用GET方法提交的数据时, 要将编码转换为 GBK 或 UTF-8.
|
1
2
|
String selProvince=request.getParameter("parProvince");
selProvince=new String(selProvince.getBytes("ISO-8859-1"),"UTF-8");
|
(2)用于使用POST方法提交数据时, 默认的字符编码是 UTF-8, 所以当接收使用 POST 方法提交的数据时, 要将编码转换为UTF-8.
|
1
2
|
String username=request.getParameter("user");
username=new String(username.getBytes("ISO-8859-1"),"UTF-8");
|
13.6.2 获取服务器的响应结果时出现中文乱码
由于 Ajax 在接收 responseText 或 responseXML 的值时是按照 UTF-8 的编码格式进行解码的, 所以如果服务器端传递的数据不是UTF-8格式, 在接收 responseText 或 responseXML 的值时, 就可能产生乱码. 解决的办法是 确保从服务器端传递的数据采用 UTF-8 的编码格式.
13.7 Ajax 重构
13.7.1 Ajax 重构的步骤
(1) AjaxRequest.js
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
var net = new Object(); // 定义一个全局变量net
// 编写构造函数net.AjaxRequest = function(url, onload, onerror, method, params) {
this.req = null;
this.onload = onload;
this.onerror = (onerror) ? onerror : this.defaultError;
this.loadDate(url, method, params);
}// 编写用于初始化XMLHttpRequest对象并指定处理函数,最后发送HTTP请求的方法net.AjaxRequest.prototype.loadDate = function(url, method, params) {
if (!method) {
method = "GET";
}
if (window.XMLHttpRequest) {
this.req = new XMLHttpRequest();
} else if (window.ActiveXObject) {
this.req = new ActiveXObject("Microsoft.XMLHTTP");
}
if (this.req) {
try {
var loader = this;
this.req.onreadystatechange = function() {
net.AjaxRequest.onReadyState.call(loader);
}
this.req.open(method, url, true);// 建立对服务器的调用
if (method == "POST") {// 如果提交方式为POST
this.req.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded"); // 设置请求头
}
this.req.send(params); // 发送请求
} catch (err) {
this.onerror.call(this);
}
}
}// 重构回调函数net.AjaxRequest.onReadyState = function() {
var req = this.req;
var ready = req.readyState;
if (ready == 4) {// 请求完成
if (req.status == 200) {// 请求成功
this.onload.call(this);
} else {
this.onerror.call(this);
}
}
}// 重构默认的错误处理函数net.AjaxRequest.prototype.defaultError = function() {
alert("错误数据\n\n回调状态:" + this.req.readyState + "\n状态: " + this.req.status);
} |
(2) 在需要使用 Ajax 的页面中使用如下语句
|
1
|
<script language="javascript" src="AjaxRequest.js"></script>
|
(3) 在使用 Ajax的页面中编写错误处理方法, 实例化 Ajax 对象的方法和回调函数.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<script language="javascript">
/**错误处理的方法**/function onerror(){
alert("您的操作有误!");
}/**实例化Ajax对象的方法**/function getInfo(){
var loader=new net.AjaxRequest("getInfo.jsp?nocache="+new Date().getTime(), deal_getInfo, onerror, "GET");
}/**回调函数**/function deal_getInfo(){
document.getElementById("showInfo").innerHTML=this.req.responseText;
}</script> |
131.7.2 使用 Ajax 重构实现实时显示公告信息
[例13.17] 实时显示公告信息. (实例位置: disc\TM\sl\13\2)
(1) AjaxRequest.js
(2) 在 index.jsp 中引用 AjaxRequest.js 文件
|
1
|
<script language="javascript" src="JS/AjaxRequest.js"></script>
|
(3) 在 index.jsp 页面中编写错误处理函数, 实例化Ajax对象的方法和回调函数.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<script language="javascript">
/**错误处理的方法**/function onerror(){
alert("您的操作有误!");
}/**实例化Ajax对象的方法**/function getInfo(){
var loader=new net.AjaxRequest("getInfo.jsp?nocache="+new Date().getTime(), deal_getInfo, onerror, "GET");
}/**回调函数**/function deal_getInfo(){
document.getElementById("showInfo").innerHTML=this.req.responseText;
}</script> |
(4) 由于要实现滚动显示公告信息, 所以还添加了<marquee>标记.
|
1
2
3
4
5
|
<div style="border: 1px solid; height: 50px; width: 200px; padding: 5px;">
<marquee direction="up" scrollamount="3">
<div id="showInfo"></div>
</marquee>
</div> |
(5) getInfo.jsp
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<%@ page language="java" contentType="text/html; charset=GB18030"
pageEncoding="GB18030"%>
<%@ page import="java.sql.*" %>
<jsp:useBean id="conn" class="com.wgh.core.ConnDB" scope="page"></jsp:useBean>
<ul><%ResultSet rs=conn.executeQuery("SELECT title FROM tb_bbsInfo ORDER BY id DESC"); //获取公告信息
if(rs.next()){
do{
out.print("<li>"+rs.getString(1)+"</li>");
}while(rs.next());
}else{
out.print("<li>暂无公告信息!</li>");
}%></ul> |
(6) 实时获取公告信息
|
1
2
3
4
|
window.onload=function(){
getInfo(); //调用getInfo()方法获取公告信息
window.setInterval("getInfo()", 60*1000*10); //每隔10分钟调用一次getInfo()方法
} |
图13.8 实时显示的公告信息

13.8 Ajax 常用实例
13.8.1 级联下拉列表
[例13.18] 级联下拉列表. (实例位置: disc\TM\sl\13\3)
(1) AjaxRequest.js
(2) index.jsp
|
1
|
<script language="javascript" src="JS/AjaxRequest.js"></script>
|
(3) 编写实例化用于异步获取省份和直辖市的 Ajax 对象的方法和回调函数.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
//获取省份和直辖市function getProvince(){
var loader=new net.AjaxRequest("ZoneServlet?action=getProvince&nocache="+new Date().getTime(),deal_getProvince,onerror,"GET");
}function deal_getProvince(){
provinceArr=this.req.responseText.split(","); //将获取的省份名称字符串分隔为数组
for(i=0;i<provinceArr.length;i++){ //通过循环将数组中的省份名称添加到下拉列表中
document.getElementById("province").options[i]=new Option(provinceArr[i],provinceArr[i]);
}
if(provinceArr[0]!=""){
getCity(provinceArr[0]); //获取市县
}
}window.onload=function(){
getProvince(); //获取省份和直辖市
} |
编写实例化用于异步获取市县的 Ajax 对象的方法和回调函数, 以及错误处理函数.
|
1
2
3
4
5
6
7
8
9
10
11
12
|
//获取市县function getCity(selProvince){
var loader=new net.AjaxRequest("ZoneServlet?action=getCity&parProvince="+selProvince+"&nocache="+new Date().getTime(),deal_getCity,onerror,"GET");
}function deal_getCity(){
cityArr=this.req.responseText.split(","); //将获取的市县名称字符串分隔为数组
document.getElementById("city").length=0; //清空下拉列表
for(i=0;i<cityArr.length;i++){ //通过循环将数组中的市县名称添加到下拉列表中
document.getElementById("city").options[i]=new Option(cityArr[i],cityArr[i]);
}
}function onerror(){} //错误处理函数
|
(4) 在省份的下拉列表的 onchange 事件中, 调用 getCity() 方法获取该省份对应的市县.
|
1
|
<select name="province" id="province" onchange="getCity(this.value)"></select>
|
|
1
2
|
<select name="city" id="city">
</select>
|
(5) ZoneServlet 中的 doGet() 方法
|
1
2
3
4
5
6
7
8
9
|
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String action=request.getParameter("action"); //获取action参数的值
if("getProvince".equals(action)){ //获取省份和直辖市信息
this.getProvince(request,response);
}else if("getCity".equals(action)){ //获取市县信息
this.getCity(request, response);
}
} |
(6) ZoneServlet 中的 getProvice() 方法
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
/** * 获取省份和直辖市
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
public void getProvince(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.setCharacterEncoding("GBK"); //设置响应的编码方式
String result="";
CityMap cityMap=new CityMap();//实例化保存省份信息的CityMap类的实例
Map<String,String[]> map=cityMap.model;//获取省份信息保存到Map中
Set<String> set=map.keySet(); //获取Map集合中的键,并以Set集合返回
Iterator it=set.iterator();
while(it.hasNext()){ //将获取的省份连接为一个以逗号分隔的字符串
result=result+it.next()+",";
}
result=result.substring(0, result.length()-1); //去除最后一个逗号
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.print(result); //输出获取的省份字符串
out.flush();
out.close();
} |
(7) ZoneServlet 中的 getCity() 方法
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
/** * 获取市县
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
public void getCity(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.setCharacterEncoding("GBK"); //设置响应的编码方式
String result="";
String selProvince=request.getParameter("parProvince"); //获取选择的省份
selProvince=new String(selProvince.getBytes("ISO-8859-1"),"GBK");
CityMap cityMap=new CityMap(); //实例化保存省份信息的CityMap类的实例
Map<String,String[]> map=cityMap.model; //获取省份信息保存到Map中
String[]arrCity= map.get(selProvince); //获取指定键的值
for(int i=0;i<arrCity.length;i++){ //将获取的市县连接为一个以逗号分隔的字符串
result=result+arrCity[i]+",";
}
result=result.substring(0, result.length()-1); //去除最后一个逗号
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.print(result); //输出获取的市县字符串
out.flush();
out.close();
} |
(8) 省份的初始化
|
1
2
3
|
window.onload=function(){
getProvince();
} |
图13.9 级联下拉列表

13.8.2 显示进度条
[例13.19] 在进行文件上传时, 显示上传进度条. (实例位置: disc\TM\sl\13\4)
(1) index.jsp. 由于要实现文件上传, 需要将表单的 enctype 属性设置为 multipart/form-data.
|
1
2
3
4
5
|
<form name="form1" enctype="multipart/form-data" method="post" action="UpLoad?action=uploadFile">
请选择上传的文件:<input name="file" type="file" size="42">
<img src="images/shangchuan.gif" width="61" height="23" onClick="deal(form1)">
<img src="images/chongzhi.gif" width="61" height="23" onClick="form1.reset();">
</form> |
(2) 添加用于显示进度条的<div>标记和显示百分比的<span>标记.
|
1
2
|
<div id="progressBar" class="prog_border" align="left"><img src="images/progressBar.jpg" width="0" height="13" id="imgProgress"></div>
<span id="progressPercent" style="width:40px;display:none">0%</span>
|
(3) 在 CSS样式表文件 style.css 中, 添加用于控制进度条样式的CSS样式.
|
1
2
3
4
5
6
7
8
9
10
11
12
|
.prog_border { height: 15px; /*高度*/
width: 255px; /*宽度*/
background: #9ce0fd; /*背景颜色*/
border: 1px solid #FFFFFF; /*边框样式*/
margin: 0;
padding: 0;
display:none; /*不显示*/
position:relative;
left:25px;
float:left; /*居左对齐*/
} |
(4) index.jsp 中的 JavaScript 函数 deal()
|
1
2
3
|
function deal(form){
form.submit(); //提交表单
timer=window.setInterval("getProgress()",500); } //每隔500毫秒获取一次上传进度
|
(5) 编写上传文件的Servlet 实现类 UpLoad. 在该Servlet 中编写实现文件上传的方法uploadFile(). 在该方法中, 调用 commons-fileupload 组件分段上传文件, 并计算上传百分比, 将其实时地保存到 Session 中.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
public void uploadFile(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=GBK");
request.setCharacterEncoding("GBK");
HttpSession session = request.getSession();
session.setAttribute("progressBar", 0); // 定义指定上传进度的Session变量
String error = "";
int maxSize = 50 * 1024 * 1024; // 单个上传文件大小的上限
DiskFileItemFactory factory = new DiskFileItemFactory(); // 基于磁盘文件项目创建一个工厂对象
ServletFileUpload upload = new ServletFileUpload(factory); // 创建一个新的文件上传对象
try {
List items = upload.parseRequest(request);// 解析上传请求
Iterator itr = items.iterator();// 枚举方法
while (itr.hasNext()) {
FileItem item = (FileItem) itr.next(); // 获取FileItem对象
if (!item.isFormField()) {// 判断是否为文件域
if (item.getName() != null && !item.getName().equals("")) {// 判断是否选择了文件
long upFileSize = item.getSize(); // 上传文件的大小
String fileName = item.getName(); // 获取文件名
// System.out.println("上传文件的大小:" + item.getSize());
if (upFileSize > maxSize) {
error = "您上传的文件太大,请选择不超过50M的文件";
break;
}
// 此时文件暂存在服务器的内存中
File tempFile = new File(fileName);// 构造临时对象
// String savePath=tempFile.getName();
// //返回上传文件在客户端的完整路径名称
// request.setAttribute("filename", savePath);
File file = new File(request.getRealPath("/upload"),
tempFile.getName()); // 获取根目录对应的真实物理路径
InputStream is = item.getInputStream();
int buffer = 1024; // 定义缓冲区的大小
int length = 0;
byte[] b = new byte[buffer];
double percent = 0;
FileOutputStream fos = new FileOutputStream(file);
while ((length = is.read(b)) != -1) {
percent += length / (double) upFileSize * 100D; // 计算上传文件的百分比
fos.write(b, 0, length); // 向文件输出流写读取的数据
session.setAttribute("progressBar", Math
.round(percent)); // 将上传百分比保存到Session中
}
fos.close();
Thread.sleep(1000); // 线程休眠1秒
} else {
error = "没有选择上传文件!";
}
}
}
} catch (Exception e) {
e.printStackTrace();
error = "上传文件出现错误:" + e.getMessage();
}
if (!"".equals(error)) {
request.setAttribute("error", error);
request.getRequestDispatcher("error.jsp")
.forward(request, response);
} else {
request.setAttribute("result", "文件上传成功!");
request.getRequestDispatcher("upFile_deal.jsp").forward(request,
response);
}
} |
(6) AjaxRequest.js
说明: 通常情况下, 在处理POST请求时, 需要将请求头设置为 application/x-www-form-urlencoded. 但是, 如果将表单的 enctype 属性设置为 multipart/form-data, 则在处理请求时, 需要将请求头设置为 multipart/form-data.
(7) 函数 getProgres() 用于实例化 Ajax 对象.
|
1
2
3
|
function getProgress(){
var loader=new net.AjaxRequest("showProgress.jsp?nocache="+new Date().Time(), deal_p, onerror, "GET");
} |
(8) showProgress.jsp
|
1
2
|
<%@page contentType="text/html" pageEncoding="GB18030"%>
${progressBar} |
(9) 回调函数
|
1
2
3
4
5
6
7
8
|
function deal_p(){
var h=this.req.responseText;
h=h.replace(/\s/g,""); //去除字符串中的Unicode空白符
document.getElementById("progressPercent").style.display=""; //显示百分比
progressPercent.innerHTML=h+"%"; //显示完成的百分比
document.getElementById("progressBar").style.display="block"; //显示进度条
document.getElementById("imgProgress").width=h*(255/100); //显示完成的进度
} |
(10) 编写 Ajax 的错误处理函数 onerror().
|
1
2
3
|
function onerror(){
alert("上传文件出错!");
} |
图13.10 带进度条的文件上传

13.9 小结
XMLHttpRequest 对象是 Ajax 的核心技术, 需要重点掌握.
如何进行 Ajax 重构需要读者重点掌握, 这在以后的项目开发中比较常用.
13.10 实践与练习
-
编写JSP程序, 在网页中显示实时走动的系统时钟. (disc\TM\sl\13\5)
-
编写JSP程序, 实时显示新闻信息. (disc\TM\sl\13\6)
-
编写JSP程序, 使用 Ajax 实现工具提示. (disc\TM\sl\13\7)
Ajax核心技术与实践
本文详细介绍Ajax的核心技术XMLHttpRequest对象的使用方法,包括初始化、发送请求、处理响应等,并通过多个实例展示如何运用Ajax改善用户体验,如实时显示公告、级联下拉列表、文件上传进度条等。
4062

被折叠的 条评论
为什么被折叠?



