读书笔记: JavaWeb从入门到精通 第13章: Ajax 技术

通过阅读本章, 你可以:

  • 了解 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" );
// or
var  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 处理服务器响应

  1. 处理字符串响应

    [例13.13] 将字符串响应显示到提示对话框中的回调函数的具体代码如下:


           

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function  getResult(){
         if (http_request.readyState == 4){     //判断请求状态
             if (http_request.status == 200){   //判断响应状态
                 alert(http_request.responseText);
             else {
                 alert( "您所请求的页面有错误!" );
             }
         }
    }

           

           

    如果需要将响应结果显示到页面的指定位置, 那么可以预先在页面的适当位置添加一个<div>或<span>标记, 并设置其id属性, 然后在回调函数中使用如下代码显示响应结果:


           

    1
    document.getElementById( "div_result" ).innerHTML=http_request.responseText;

           

       

  2. 处理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 实践与练习

  1. 编写JSP程序, 在网页中显示实时走动的系统时钟. (disc\TM\sl\13\5)

  2. 编写JSP程序, 实时显示新闻信息. (disc\TM\sl\13\6)

  3. 编写JSP程序, 使用 Ajax 实现工具提示. (disc\TM\sl\13\7)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值