首页 > 技术文章 > Ajax实现动态的二级级联菜单

likailan 2013-09-18 17:32 原文

 今天花了点时间用Ajax实现了一个二级级联菜单。整理总结一下。为了把重点放在Ajax和级联菜单的实现上,本文省略了数据库建表语句和操作数据库的代码!

数据库建表语句就不帖出来了。主要有两张表,区域表:district。街道表:street。区域表和街道表是一对多关系,一个区域可以有零到多个街道,一条街道属于一个区域。数据如下:

        

 

 页面代码 index.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="js/js.js"></script>
<title>Insert title here</title>
</head>
<body>
    <select id="district" onchange="cascade(this.value)" >
        <option value="-1">请选择</option>
        <c:forEach items="${districts }" var="district">
            <option value="${district.id }">${district.name }</option>
        </c:forEach>
    </select>
    <select id="street" onchange="alert(this.value)">
        <option>请选择</option>
    </select>
</body>
</html>

 

初始化主页面一级菜单列表的Servlet代码  InitServlet.java:

 1 package cascade.servlet;
 2 import java.io.IOException;
 3 import java.util.List;
 4 
 5 import javax.servlet.ServletException;
 6 import javax.servlet.http.HttpServlet;
 7 import javax.servlet.http.HttpServletRequest;
 8 import javax.servlet.http.HttpServletResponse;
 9 
10 import cascade.entity.District;
11 import cascade.service.DistrictService;
12 
13 
14 public class InitServlet extends HttpServlet{
15 
16     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
17         doPost(req, resp);
18     }
19 
20     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
21         req.setCharacterEncoding("UTF-8");
22         resp.setCharacterEncoding("UTF-8");
23         /*
24          * DistrictService  ds为操作数据库的对象.
25          * 调用该对象的getAllDistrict()方法,可以从数据库中取得所有的区域信息,封装为List<District>对象,并返回。
26          * 其中District是数据库District表的实体类 
27          * 为了把重点放在Ajax和级联菜单的实现上,本文省略了操作数据库的代码!!!
28          */
29         DistrictService ds = new DistrictService();
30         List<District> districts = ds.getAllDistrict();
31         //List<District>对象存在request范围中,并转向到主页
32         req.setAttribute("districts", districts);
33         req.getRequestDispatcher("index.jsp").forward(req, resp);
34     }
35     
36 }

 

web配置文件  web.xml:

在web.xml中添加如配置代码

  <servlet>
      <servlet-name>init</servlet-name>
      <servlet-class>cascade.servlet.InitServlet</servlet-class>
  </servlet>
  <servlet-mapping>
      <servlet-name>init</servlet-name>
      <url-pattern>/index</url-pattern>
  </servlet-mapping>

此时,在浏览器中访问 InitServlet.java (假设项目名为AjaxCascade,访问地址就是:http://localhost:8080/AjaxCascade/index

数据库的区域数据就已经填到第一级菜单项中了。 

此时页面的源代码可以看到下拉菜单(select)里每一项(option)的value也是从数据库里取到的:

    <select id="district" onchange="cascade(this.value)" >
        <option value="-1">请选择</option>
            <option value="1001">天河区</option>        
            <option value="1002">越秀区</option>        
            <option value="1003">海珠区</option>        
            <option value="1005">番禺区</option>        
            <option value="1000">白云区</option>        
            <option value="1004">花都区</option>        
    </select>

 

现在开始现实街道下拉菜单(也就是页面上id为street的select)的动态获取。

我们知道,但区域菜单的值改变时,街道菜单的值也要动态的发生改变(如区域选择“天河区”,街道菜单就应该动态加上“中山大道“这一项,如区域选择“海珠区”,街道菜单就应该动态加上“昌岗路”,并删除“中山大道“)。

为了现实上面所说的功能,要为区域菜单添加 onchange 事件的处理:onchange="cascade(this.value)" 当区域菜单值发生改变时调用cascade方法,并把自己的value属性传递进去。

cascade代码如下  js.js:

 1 //XMLHttpRequest组件
 2 var xhs;
 3 //区域菜单的值发生改变时调用该方法,并把区域菜单当前的value传递进来
 4 function cascade(id){
 5     //当id不大于0时,说明当前选择的是“请选择”这一项,则不做操作
 6     if(id>0){
 7         //请求字符串,把区域的id作为页面参数传到后台
 8         var url="cascade?id="+id;
 9         //创建XMLHttpRequest组件
10         xhs=new XMLHttpRequest();
11         //设置回调函数,processReuqest方法的定义在下面
12         xhs.onreadystatechange=processReuqest;
13         //打开与服务器的地址连接
14         xhs.open("post", url, true);
15         //发送请求
16         xhs.send(null);
17     }
18 }
19 
20 //processReuqest方法作为回调方法
21 function processReuqest(){
22     if(xhs.readyState==4){
23         if(xhs.status==200){
24             //创建新的select节点
25             var newSelect=document.createElement("select");
26             newSelect.id="street";
27             //为新创建的select节点添加onchange事件,以便测试用
28             newSelect.onchange=function test(){
29                 alert(this.value);
30             };
31             //为新创建的select节点添加option节点
32             var op=document.createElement("option");
33             op.value=-1;
34             op.innerHTML="请选择";
35             newSelect.appendChild(op);
36             //得到完成请求后返回的字串符
37             var str = xhs.responseText;
38             //根据返回的字符串为新创建的select节点添加option节点
39             var arr1=str.split(",");
40             for(var i=0;i<arr1.length;i++){
41                 var arr2=arr1[i].split("=");
42                 var child=document.createElement("option");
43                 child.innerHTML=arr2[1];
44                 child.value=arr2[0];
45                 newSelect.appendChild(child);
46             }
47             //用新select节点替换旧的select节点
48             var select = document.getElementById("street");
49             document.body.replaceChild(newSelect, select);
50         }
51     }
52 }

XMLHttpRequest 对象:XMLHttpRequest 对象提供了对 HTTP 协议的完全的访问,包括做出 POST 和 HEAD 请求以及普通的 GET 请求的能力。XMLHttpRequest 可以同步或异步地返回 Web 服务器的响应,并且能够以文本或者一个 DOM 文档的形式返回内容。尽管名为 XMLHttpRequest,它并不限于和 XML 文档一起使用:它可以接收任何形式的文本文档。XMLHttpRequest 对象是名为 AJAX 的 Web 应用程序架构的一项关键功能。

创建XMLHttpRequest 对象:尽管大部份浏览器都支持 new XMLHttpRequest() 来得到 XMLHttpRequest 对象,但IE浏览器和其他Web浏览器返回的 XMLHttpRequest 对象实例是不相同的,为了支持更多浏览器,建议编码时如下所示的代码创建XMLHttpRequest 实例。

function createXmlHttpRequest(){
    if(window.ActiveXObject){
        return new ActiveXObject("Microsoft.XMLHTTP");
    }else if(window.XMLHttpRequest){
        return new XMLHttpRequest();
    }
}

open(method,url,async)方法:建立于服务器的连接,method参数指定请求的HTTP方法。URL 参数指定请求的地址。acync 参数指定是否使用异步请求,其值为 true 或 false。

send(content)方法:发送请求,content参数指定请求的参数。当send方法不配置参数,即xhr.send()时,在IE中能够正常运行,但在Firefox中却不能,所以,建议最好加上null 。

onreadystatechange属性:指定XMLHttpRequest对象的回调函数。onreadystatechange属性作用与文本框的onblur等属性一样,是事件处理属性,即当 XMLHttpRequest 的状态发生改变时,XMLHttpRequest 对象都会解发onreadystatechange所指定的函数。

readyState属性:XMLHttpRequest的状态信息。XMLHttpRequest对象有如下几种状态。

0:XMLHttpRequest对象没有完成初始化,此时,已经创建一个XMLHttpRequest对象,但是还没有初始化。

1:XMLHttpRequest对象开始发送请求,此时,代码已经调用open()方法并且XMLHttpRequest已经准备好把一个请求发送到服务器。

2:XMLHttpRequest对象的请求发送完成。此时,已经通过send()方法把一个请求发到服务器端,但是还没有收到一个响应。

3:XMLHttpRequest对象开始读取响应。此时,已经接收到HTTP响应头部信息,但是消息体部分还没有完全接收结束。

4:XMLHttpRequest对象读取响应结束。些时,响应已被完全接收。

status属性:HTTP的状态码,仅当readyState的值为3或4时,status属性才可用。status属性如下。

200:服务器响应正常。

400:无法找到请求的资源。

403:没有访问权限。

404:访问资源不存在。

500:服务器内部错误。

responseText属性:获得响应的文本。当readyState值为0、1、2时,reponseText包含一下空字符串。当readyState值为3时,响应中包含还未完成的响应信息。当readyState值为4时,readyState包含完整的响应信息。

 

接下来是后台代码的实现,从cascade方法可以看到,XMLHttpRequest对象是请求到名为cascade的地址上的。

CascadeServlet.java:

 1 package cascade.servlet;
 2 import java.io.IOException;
 3 import java.io.PrintWriter;
 4 import java.util.List;
 5 
 6 import javax.servlet.ServletException;
 7 import javax.servlet.http.HttpServlet;
 8 import javax.servlet.http.HttpServletRequest;
 9 import javax.servlet.http.HttpServletResponse;
10 
11 import cascade.entity.District;
12 import cascade.entity.Street;
13 import cascade.service.StreetService;
14 
15 
16 public class CascadeServlet extends HttpServlet{
17 
18     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
19         doPost(req, resp);
20     }
21 
22     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
23         req.setCharacterEncoding("UTF-8");
24         resp.setCharacterEncoding("UTF-8");
25         int id =Integer.parseInt(req.getParameter("id"));
26         District district=new District();
27         district.setId(id);
28         
29         /*
30          * StreetService  ss为操作数据库的对象.
31          * 调用该对象的getAllStreet()方法,可以从数据库中取得所有的区域信息,封装为List<Street>对象,并返回。
32          * 其中Street是数据库Street表的实体类 
33          * 为了把重点放在Ajax和级联菜单的实现上,本文省略了操作数据库的代码!!!
34          */
35         StreetService ss=new StreetService();
36         List<Street> streets=ss.getAllStreet(district);
37         //把得到的街道对象集合拼接成字符串文本
38         StringBuffer sb=new StringBuffer();
39         for(int i=0;i<streets.size();i++){
40             sb.append(streets.get(i).getId()).append("=").append(streets.get(i).getName());
41             if(i!=streets.size()-1){
42                 sb.append(",");
43             }
44         }
45         //servlet不转向或重定向到任何页面,使用resp.getWriter().print()方法可以把文本响应给XMLHttpRequest对象
46          PrintWriter out = resp.getWriter();
47          out.print(sb.toString());
48          out.flush();
49          out.close();
50     }
51     
52 }

 

在web.xml中的配置:

  <servlet>
      <servlet-name>cascade</servlet-name>
      <servlet-class>cascade.servlet.CascadeServlet</servlet-class>
  </servlet>
  <servlet-mapping>
      <servlet-name>cascade</servlet-name>
      <url-pattern>/cascade</url-pattern>
  </servlet-mapping>

 

完工,效果如下:

 

推荐阅读