Ajax实现多级联动菜单

最近在重温Ajax技术,就顺手拿起了当年的《Ajax经典案例开发大全》来温习。发现里面有些错误,现在就指出来,这样可以帮助后学者少走弯路。主要的技术有:MysqL、JDBC、JSON、Ajax、JSP。其实本文不算原创,主要内容还是摘自《Ajax经典案例开发大全》。

1.数据库设计

[sql] view plaincopyprint?

drop database if exists mydb;
create database mydb character set gbk;

--多级联动菜单
use mydb;
drop table if exists select_menu;
create table select_menu(
id varchar(255) not null default '',
text varchar(255) not null,
pid varchar(255) not null,
seq int(11) not null default 0,
primary key (id)
)ENGINE=InnoDB DEFAULT CHARSET=gbk;

insert into select_menu values('A1','列表A选项1','INIT',1);
insert into select_menu values('A2','列表A选项2',2);
insert into select_menu values('B11','列表B选项11','A1',1);
insert into select_menu values('B12','列表B选项12',2);
insert into select_menu values('B13','列表B选项13',3);
insert into select_menu values('B21','列表B选项21','A2',1);
insert into select_menu values('B22','列表B选项22',2);
insert into select_menu values('C111','列表C选项111','B11',1);
insert into select_menu values('C112','列表C选项112',2);
insert into select_menu values('C121','列表C选项121','B12',1);
insert into select_menu values('C122','列表C选项122',2);
insert into select_menu values('C131','列表C选项131','B13',1);
insert into select_menu values('C132','列表C选项132',2);
insert into select_menu values('C211','列表C选项211','B21',1);
insert into select_menu values('C212','列表C选项212',2);
insert into select_menu values('C221','列表C选项221','B22',1);
insert into select_menu values('C222','列表C选项222',2);

2.连接数据库的工具类

[java] view plaincopyprint?

package com.lanp.ajax.db;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.sqlException;

/**
* 连接数据库的工具类,被定义成不可继承且是私有访问
* @author lanp
* @since 2012-2-29 22:27
*/
public final class dbutils {
private static String url = "jdbc:MysqL://localhost:3306/mydb?characterEncoding=gbk";
private static String user = "root";
private static String psw = "root";

private static Connection conn;

static {
try {
Class.forName("com.MysqL.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printstacktrace();
throw new RuntimeException(e);
}
}

private dbutils() {

}

/**
* 获取数据库的连接
* @return conn
*/
public static Connection getConnection() {
try {
conn = DriverManager.getConnection(url,user,psw);
} catch (sqlException e) {
e.printstacktrace();
throw new RuntimeException(e);
}
return conn;
}

/**
* 释放资源
* @param conn
* @param pstmt
* @param rs
*/
public static void closeResources(Connection conn,PreparedStatement pstmt,ResultSet rs) {
if(null != rs) {
try {
rs.close();
} catch (sqlException e) {
e.printstacktrace();
throw new RuntimeException(e);
} finally {
if(null != pstmt) {
try {
pstmt.close();
} catch (sqlException e) {
e.printstacktrace();
throw new RuntimeException(e);
} finally {
if(null != conn) {
try {
conn.close();
} catch (sqlException e) {
e.printstacktrace();
throw new RuntimeException(e);
}
}
}
}
}
}
}
}

3.select_menu.html页面

[html] view plaincopyprint?

<!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">
<title>多级联动菜单</title>

<script type="text/javascript">
var xmlHttp;//用于保存XMLHttpRequest对象的全局变量
var targetSelId;//用于保存要更新选项的列表ID
var selArray = new Array();//用于保存级联菜单ID的数组,《Ajax经典案例开发大全》中没有= new Array()代码

//用于创建XMLHttpRequest对象
function createXmlHttp() {
if(window.XMLHttpRequest) {
xmlHttp = new XMLHttpRequest();
} else {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
}

//获取列表选项的调用函数
function buildSelect(selectedId,targetId) {
if("" == selectedId) {//selectedId为空串表示选中了认项
clearSubSel(targetId);//清楚目标列表及下级列表中的值
return;//直接结束调用,不必向服务器请求信息
}

targetSelId = targetId;//将传入的目标列表ID赋值给targetSelId变量
createXmlHttp();//创建XMLHttpRequest对象
xmlHttp.onreadystatechange = buildSelectCallBack;//设置回调函数
xmlHttp.open("GET","select_menu.jsp?selectedId="+selectedId,true);
xmlHttp.send(null);
}

//获取列表选项的回调函数
function buildSelectCallBack() {
if(4 == xmlHttp.readyState) {
//将从服务器获得的文本转为对象直接量
var optionsInfo = eval("(" + xmlHttp.responseText + ")");
var targetSelNode = document.getElementById(targetSelId);
clearSubSel(targetSelId);
//遍历对象直接量中的成员
for(var o in optionsInfo) {
//在目标列表追加新的选项
targetSelNode.appendChild(createOption(o,optionsInfo[o]));
}
}
}

//根据传入的value和text创建选项
function createOption(value,text) {
var opt = document.createElement("option");//创建一个option节点
opt.setAttribute("value",value);//设置value
opt.appendChild(document.createTextNode(text));//给节点加入文本信息
return opt;
}

//清除传入的列表节点内所有选项
function clearOptions(selNode) {
selNode.options.length = 1;
selNode.options[0].selected = true;
}

//初始化列表数组,《Ajax经典案例开发大全》中该方法代码是有误没有实现真正的初始化
function initSelArray(selA,selB,selC) {
selArray[0] = selA;
selArray[1] = selB;
selArray[2] = selC;
}

//清除下级子列表选项
function clearSubSel(targetId) {
var len = selArray.length;
for(var i=0;i<len;i++) {
var j = 0;
if(selArray[i] == targetId) {
j = i;
break;
}
}
for(; j<len; j++) {
clearOptions(document.getElementById(selArray[j]));
}
//《Ajax经典案例开发大全》中该方法代码是有误,不能实现下级列表全部清除功能,且代码冗余,如下示:
//var canClear = false;
//for(var i=0; i<selArray.length; i++) {
//if(selArray[i] == targetId) {
//canClear = true;
//}
//if(canClear) {
//clearOptions(document.getElementById(selArray[i]));
//}
//}
}
</script>
</head>
<!-- 页面加载完毕做2件事:1.初始化列表数组,2.为第一个列表赋值 -->
<body onload="initSelArray('selA','selB','selC');buildSelect('INIT','selA')">
<h1>多级联动菜单</h1>
<table>
<tr>
<td>列表A</td>
<td>
<select name="selA" id="selA" onchange="buildSelect(this.value,'selB')">
<option value="" selected>-------请选择-------</option>
</select>
</td>
</tr>

<tr>
<td>列表B</td>
<td>
<select name="selB" id="selB" onchange="buildSelect(this.value,'selC')">
<option value="" selected>-------请选择-------</option>
</select>
</td>
</tr>

<tr>
<td>列表C</td>
<td>
<select name="selC" id="selC">
<option value="" selected>-------请选择-------</option>
</select>
</td>
</tr>
</table>
</body>
</html>

4.select_menu.jsp后台服务

[html] view plaincopyprint?

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="java.sql.*,com.lanp.ajax.db.dbutils" %>
<%!
//访问数据库取得下级选项信息
String getoptions(String selectedId) {
int counter = 0;
StringBuffer opts = new StringBuffer("{");
String sql = "select * from select_menu where pid=? order by seq asc";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = dbutils.getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1,selectedId);
rs = pstmt.executeQuery();

while(rs.next()) {
//如果不是第一项,追加一个","用于分隔选项
if(counter > 0) {
opts.append(",");
}
opts.append("'");
opts.append(rs.getString("id"));
opts.append("':'");
opts.append(rs.getString("text"));
opts.append("'");
counter++;
}
} catch(sqlException e) {
System.out.println(e.toString());
} finally {
dbutils.closeResources(conn,pstmt,rs);
}
opts.append("}");
System.out.println(opts.toString());
return opts.toString();
}
%>

<% out.clear(); String selectedId = request.getParameter("selectedId"); String optionsInfo = getoptions(selectedId); out.print(optionsInfo); %> OK,TKS!

相关文章

IE6是一个非常老旧的网页浏览器,虽然现在很少人再使用它,但...
PHP中的count()函数是用来计算数组或容器中元素的个数。这个...
使用 AJAX(Asynchronous JavaScript and XML)技术可以在不...
Ajax(Asynchronous JavaScript and XML)是一种用于改进网页...
本文将介绍如何通过AJAX下载Excel文件流。通过AJAX,我们可以...
Ajax是一种用于客户端和服务器之间的异步通信技术。通过Ajax...