草履虫在路上

记录生活,学习的点点滴滴.致力于Web2.0学习,邮箱:caolvchong At gmail.com

2007-10-4 1:27:14

« javascript对象编程规范(个人)日子,日子 »

AJAX实现4级联动菜单

*项目名称:AJAX打造4级联动菜单
*作者:草履虫
*联系:caolvchong@gmail.com
*时间:2007-10-3
*起因:
应一位网友的请求
*开发平台:Windows2003 IIS6.0 Access数据库
*工具:Emeditor,Access2003(数据库)
*测试平台:Firefox2.0,Opera9.2,IE6.0,IE7.0
*演示地址:http://finish.3322.org/4_select/index.asp(短期有效,在本机上,可能访问不顺畅)
*原文地址:http://cceer.xmu.edu.cn/blog/view.asp?id=83
*:文件结构:
数据库结构:四级联动,用四张表存各级的数据(加个conn.asp连接数据库)
对于数据库结构,其实这4个表可以放到一个表中,而不必要四个表.只要添加一个字段表名等级即可.数据库结构去附件中看看吧,加了注释了.
function.asp:数据处理文件
deal.asp:ajax后台处理文件
ajax.js:ajax交互文件核心
index.asp:表现页面
*和之前写的3级联动区别:
区别是蛮大的,采用了新的算法,代码重用性也高了,一定程度上分离了业务逻辑和表现逻辑等等.这些看代码自己体会吧
如果你要加以改造,或者实现任意级的联动,修改起来应该不难,因为这次实现联动菜单的算法已经考虑到这种情况.

*下载地址:4_select.rar
或者:http://finish.3322.org/4_select.rar

index.asp代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>四级联动菜单</title>
<script type="text/javascript" src="ajax.js"></script>
<!--#include file="function.asp"-->
</head>
<body>
<select name="xiaoqu" onchange="ajax_change(this.value,'yuanxi',$('yuanxi'));">
  <option value="-1">-----请选择校区----</option>
  <%
    xiaoqu=read_xiaoqu()
    if IsArray(xiaoqu) then
      for i=lbound(xiaoqu) to ubound(xiaoqu)
        response.write "<option value="&xiaoqu(i)(0)&">"&(i+1)&"├"&xiaoqu(i)(1)&"</option>"
      next
    end if
  %>
</select>
<span id="yuanxi">
  <select>
    <option value="-1">-----请选择院系-----</option>
  </select>
</span>
<span id="zhuanye">
  <select>
    <option value="-1">-----请选择专业-----</option>
  </select>
</span>
<span id="banji">
  <select>
    <option value="-1">-----请选择班级-----</option>
  </select>
</span>
</body>
</html>

function.asp代码:

<!--#include file="conn.asp"-->
<%
'--->函数:read_xiaoqu()
'   功能:返回所有的校区
'   参数:无
'   返回值:
'    返回一个数组,格式为((校区id,校区名称),(校区id,校区名称),....)
'   调用:
'    xiaoqu=read_xiaoqu()  这里,xiaoqu是变量名,可以任意取
'   使用:
'    xiaoqu(0)(0)----对应第一个记录的id
'    xiaoqu(0)(1)----对应第一个记录的name,依此类推
  function read_xiaoqu()
    sql="select id,name from xiaoqu"
    set rs=server.createobject("adodb.recordset")
    on error resume next
    rs.open sql,conn,1,1
    if err<>0 then
      read_xiaoqu=-1
    else
      dim xiaoqu_array()
      dim i:i=0
      while not rs.eof
        redim preserve xiaoqu_array(i)
        xiaoqu_id=rs("id")
        xiaoqu_name=rs("name")
        xiaoqu_array(i)=array(xiaoqu_id,xiaoqu_name)
        i=i+1
        rs.movenext
      wend
      read_xiaoqu=xiaoqu_array
    end if
  end function
          
'--->函数:read_table_data(id,table_name)
'   功能:返回对应表的数据
'   参数:
'    id,对应的父亲id
'    table_name:对应的表名
'    比如:选择了校区的id=2,table_name选择院系yuanxi.因为要从表院系中读取对应pid为2的数据
'   返回值:
'    返回一个数组,格式为((id,name),(id,name),....)
'   调用:
'    data=read_table_data(id,table_name)    这里,data是变量名,可以任意取
'   使用:
'    data(0)(0)----对应第一个记录的id
'    data(0)(1)----对应第一个记录的name,依此类推
  function read_table_data(id,table_name)
    sql="select id,name from "&table_name&" where pid="&id
    set rs=server.createobject("adodb.recordset")
    on error resume next
    rs.open sql,conn,1,1
    if err<>0 then
      read_table_data=-1
    else
      dim data_array()
      dim i:i=0
      while not rs.eof
        redim preserve data_array(i)
        id=rs("id")
        name=rs("name")
        data_array(i)=array(id,name)
        i=i+1
        rs.movenext
      wend
      read_table_data=data_array
    end if
  end function
  
  
'--->函数:option_default(table_name)
'   功能:返回选择框的头部
'   参数:
'    table_name:对应的表名
'   返回值:
'    返回一个字符串
'   调用:
'    str=option_default(table_name)    这里,str是变量名,可以任意取
  function option_default(table_name)
    select case table_name
      case "yuanxi":
        str="请选择院系"
      case "zhuanye":
        str="请选择专业"
      case "banji":
        str="请选择班级"
      case else
        str="出错了"
    end select
    option_default=str
  end function
  

'--->函数:set_next_node(table_name)
'   功能:返回当前生成节点的下一个节点的名字
'   参数:
'    table_name:对应的表名
'   返回值:
'    返回一个字符串
'   调用:
'    next_node=set_next_node(table_name)    这里,next_node是变量名,可以任意取
  function set_next_node(table_name)
    select case table_name
      case "yuanxiao":
        node="yuanxi"
      case "yuanxi":
        node="zhuanye"
      case "zhuanye":
        node="banji"
    end select
    set_next_node=node
  end function
%>

ajax.js代码:

var $=function(tagId){
  return document.getElementById(tagId);
}
//------------------------------------------------------------  
//id:接收的id值(用来提供搜索条件)
//type:接收的类型(用来提供表名)
//node:接收的节点(用来反馈数据插入),通常是指定后面一个select
  function ajax_change(id,type,node){
  var xmlhttp;
  try{
    xmlhttp=new XMLHttpRequest();
  }catch(e){
    xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }
  xmlhttp.onreadystatechange=function(){
  if (xmlhttp.readyState==4){
    if (xmlhttp.status==200){
      var data=xmlhttp.responseText;
      node.innerHTML=data;
    }else{
      node.innerHTML="<select><option>-----您未进行上一级选择-----</option></select>";
      }
    }
  }
  xmlhttp.open("post","deal.asp", true);
  xmlhttp.setRequestHeader('Content-type','application/x-www-form-urlencoded');
  xmlhttp.send("id="+escape(id)+"&type="+escape(type));
}

deal.asp代码:

<%@LANGUAGE="VBSCRIPT" CODEPAGE="65001"%>
<!--#include file="function.asp"-->
<%
  id=request.form("id")
  table_name=request.form("type")
  next_node=set_next_node(table_name)
  if table_name <> "banji" then
    response.write "<select name="""&table_name&""" onchange=""ajax_change(this.value,'"&next_node&"',$('"&next_node&"'));"">"
  else
    response.write "<select name="""&table_name&""">"
  end if
  str=option_default(table_name)
  response.write "<option value=""-1"" selected>---请选择"&str&"---</option>"
  data=read_table_data(id,table_name)
  if IsArray(data) then
    for i=lbound(data) to ubound(data)
      response.write "<option value="&data(i)(0)&">"&(i+1)&"├"&data(i)(1)&"</option>"
    next
  end if
  response.write "</select>"
%>



============================================================ 今天补充下昨天晚上最先写的代码.更简洁,思路也清晰符合思维,但是IE不支持(就是option无法innerHTML到select中去) index.asp(注意,前一个select和后一个select必须连在一起,即< select ...>这种格式,否则取不到nextSibling)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>四级联动菜单</title>
<script type="text/javascript" src="ajax.js"></script>
<!--#include file="function.asp"-->
</head>
<body>
<select name="xiaoqu" onchange="ajax_change(this.value,this.nextSibling);">
  <option value="-1">-----请选择校区----</option>
  <%
    xiaoqu=read_xiaoqu()
    if IsArray(xiaoqu) then
      for i=lbound(xiaoqu) to ubound(xiaoqu)
        response.write "<option value="&xiaoqu(i)(0)&">"&(i+1)&"├"&xiaoqu(i)(1)&"</option>"
      next
    end if
  %>
</select><select name="yuanxi" id="yuanxi" onchange="ajax_change(this.value,this.nextSibling);">
  <option value="-1">-----请选择院系-----</option>
</select><select name="zhuanye" id="zhuanye" onchange="ajax_change(this.value,this.nextSibling);">
  <option value="-1">-----请选择专业-----</option>
</select><select name="banji" id="banji">
  <option value="-1">-----请选择班级-----</option>
</select>
</body>
</html>

ajax.js

//------------------------------------------------------------  
//id:接收的id值(用来提供搜索条件)
//node:接收的节点(用来反馈数据插入),通常是指定后面一个select
  function ajax_change(id,node){
  var xmlhttp;
  try{
    xmlhttp=new XMLHttpRequest();
  }catch(e){
    xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }
  xmlhttp.onreadystatechange=function(){
  if (xmlhttp.readyState==4){
    if (xmlhttp.status==200){
      var data=xmlhttp.responseText;
      alert(data)
      node.innerHTML=data;
    }else{
      node.innerHTML="出现错误";
      }
    }
  }
  xmlhttp.open("post","deal.asp", true);
  xmlhttp.setRequestHeader('Content-type','application/x-www-form-urlencoded');
  xmlhttp.send("id="+escape(id)+"&type="+escape(node.name));
}

deal.asp

<%@LANGUAGE="VBSCRIPT" CODEPAGE="65001"%>
<!--#include file="function.asp"-->
<%
  id=request.form("id")
  table_name=request.form("type")
  str=option_default(table_name)
  response.write "<option value=""-1"" selected>-----请选择"&str&"-----</option>"
  data=read_table_data(id,table_name)
  if IsArray(data) then
    for i=lbound(data) to ubound(data)
      response.write "<option value="&data(i)(0)&">"&(i+1)&"├"&data(i)(1)&"</option>"
    next
  end if
%>

function.asp

<!--#include file="../conn.asp"-->
<%
'--->函数:read_xiaoqu()
'   功能:返回所有的校区
'   参数:无
'   返回值:
'    返回一个数组,格式为((校区id,校区名称),(校区id,校区名称),....)
'   调用:
'    xiaoqu=read_xiaoqu()  这里,xiaoqu是变量名,可以任意取
'   使用:
'    xiaoqu(0)(0)----对应第一个记录的id
'    xiaoqu(0)(1)----对应第一个记录的name,依此类推
  function read_xiaoqu()
    sql="select id,name from xiaoqu"
    set rs=server.createobject("adodb.recordset")
    on error resume next
    rs.open sql,conn,1,1
    if err<>0 then
      read_xiaoqu=-1
    else
      dim xiaoqu_array()
      dim i:i=0
      while not rs.eof
        redim preserve xiaoqu_array(i)
        xiaoqu_id=rs("id")
        xiaoqu_name=rs("name")
        xiaoqu_array(i)=array(xiaoqu_id,xiaoqu_name)
        i=i+1
        rs.movenext
      wend
      read_xiaoqu=xiaoqu_array
    end if
  end function
          
'--->函数:read_table_data(id,table_name)
'   功能:返回对应表的数据
'   参数:
'    id,对应的父亲id
'    table_name:对应的表名
'    比如:选择了校区的id=2,table_name选择院系yuanxi.因为要从表院系中读取对应pid为2的数据
'   返回值:
'    返回一个数组,格式为((id,name),(id,name),....)
'   调用:
'    data=read_table_data(id,table_name)    这里,data是变量名,可以任意取
'   使用:
'    data(0)(0)----对应第一个记录的id
'    data(0)(1)----对应第一个记录的name,依此类推
  function read_table_data(id,table_name)
    sql="select id,name from "&table_name&" where pid="&id
    set rs=server.createobject("adodb.recordset")
    on error resume next
    rs.open sql,conn,1,1
    if err<>0 then
      read_table_data=-1
    else
      dim data_array()
      dim i:i=0
      while not rs.eof
        redim preserve data_array(i)
        id=rs("id")
        name=rs("name")
        data_array(i)=array(id,name)
        i=i+1
        rs.movenext
      wend
      read_table_data=data_array
    end if
  end function
  
  
'--->函数:option_default(table_name)
'   功能:返回选择框的头部
'   参数:
'    table_name:对应的表名
'   返回值:
'    返回一个字符串
'   调用:
'    str=option_default(table_name)    这里,str是变量名,可以任意取
  function option_default(table_name)
    select case table_name
      case "yuanxi":
        str="院系"
      case "zhuanye":
        str="专业"
      case "banji":
        str="班级"
      case else
        str="出错了"
    end select
    option_default=str
  end function
%>

  • quote 15.饕餮
  • 乱码问题没有解决!
    如果我选了一值后!
    再选择最上面那个后,再选其他的就会出现乱码!!!!
    讨厌死了!
  • 2008-8-15 12:21:38 回复该留言
  • quote 13.无情
  • 当重新选择第二项的时候,第三项的值为空,但是第四项的值还是上一次选的值,请问有什么办法修改吗?
  • 2008-6-26 11:16:20 回复该留言
  • quote 12.Jooner
  • 选择时有乱码.........请速解决...

    麻烦发我邮箱.....

    doctor219@zj.com
  • 2008-4-23 17:08:31 回复该留言
  • quote 10.andy
  • 说简单点就是让这四个下拉框在打开页面时就以经有值了,默认不需要再去选择了!
    不管是有四个值给每个下拉框赋一个值,
    还是用最后一个值去推算前面下拉框的值!
    只要能实现四个下拉框有默认选中项就可以了!
    em13em13em13
    草履虫 于 2007-12-27 13:24:40 回复
    ajax返回的数据的option加一个selected就有默认值了.使用ajax最好能够理解下程序,这样修改起来就简单很多了.em17
    andy 于 2007-12-28 11:42:55 回复
    我知道要在deal.asp里改!
    option 要加selected
    但这四个值是从库里读出来的,是活动的,我不太明白怎样将值传给这个页面!

    if IsArray(data) then
    for i=lbound(data) to ubound(data)
    response.write "<option value="&data(i)(0)&">"&(i+1)&"├"&data(i)(1)&"</option>"
    next
    end if
  • 2007-12-28 11:42:55 回复该留言
  • quote 9.andy
  • 不是啊!难道是我表述有问题???怎么说呢!
    简单的说就是赋初使值!
    例如说:我新加了一篇文章,后来发现文章内容有问题,想修改一下里面的内容!我可以将新闻标题,发布日期,发布人,文章详细内容什么的提出来放到文本框!但这四个下拉框的植我不知道怎么赋,让下拉框里的某一项值Selected,我不可能又把四级下拉菜单重新选一次值吧!应该可以从数据库里把库里存的值赋给各个下拉框吧?不知道这样说您是否明白?!
  • 2007-12-26 10:38:27 回复该留言
  • quote 8.草履虫
  • http://cceer.xmu.edu.cn/blog/
  • 呵呵,你没有明白程序的含义.每次触发只涉及第一层子级的变化,这个是目的.
    至于你说的,其实更简单一些,第一层onchange则全部读出所有的孩子,但是这样背离了程序的原来目的,不是AJAX的初衷了.
    andy 于 2007-12-27 10:27:32 回复
    晕!我指的“重置当前级以下的所有SELECT”
    是将当前SELECT下拉框以下的值赋空!而不是第一层onchange则全部读出所有的孩子,
    要不然第一层Select值发生了改变,但最后两个下拉框还是上次的值!这就会有造成错误的可能!
  • 2007-12-27 10:27:32 回复该留言
  • quote 7.andy
  • 好像还有一点小缺憾,就是不能“重置当前级以下的所有SELECT”,例如我把所有的下拉框选过之后,觉得错了,重选了第一级的下拉框,最后的两个下拉框的值不能重置,希望能更正一下!
    em06
  • 2007-12-25 14:52:48 回复该留言
  • quote 6.草履虫
  • http://cceer.xmu.edu.cn/blog/
  • 你说的可否是只要选一次,后面都出现默认值?em13
    andy 于 2007-12-23 22:57:21 回复
    不是啊·!我是说,怎么说呢!简单的说就是赋初使值!!例如说:我新加了一篇文章,后来发现文章内容有问题,想修改一下里面的内容!我可以将新闻标题,发布日期,发布人什么的提出来放到文本框!但这四个下拉框的植我不知道怎么赋,我不可能又把四级下拉菜单重新选一次值吧!应该可以从数据库里把库里存的值赋给各个下拉框吧?不知道这样说您是否明白?!
  • 2007-12-23 22:57:21 回复该留言
  • quote 5.andy
  • 博主真厉害呀!代码写得相当好!!
    不过有一点想请问!!不过有一点想请问你一下!在修改页面,我如何将值传进去,让四个下拉框里有默认值,而不要再重选一次呀??em09
  • 2007-12-21 23:05:38 回复该留言
  • quote 3.david
  • dom操作的问题,建议看看这篇文章:
    http://www.blueidea.com/tech/web/2003/1161.asp
  • 2007-10-12 16:45:53 回复该留言
  • quote 2.还是我
  • 真的很佩服你。你应该是学计算机专业的吧,懂那么,太羡慕了。很多东西都是自己一点点查资料慢慢东拼西凑的,真的很羡慕你会的这些东西,实际应用太重要了,可惜自己现在想系统的学点东西太难了,呵。
    也真的感激你,代码看到了,已经用上了!很简洁实用高效的。
    希望有机会再向你请教一些东西,你太厉害了。包括你的C,ASP,AJAX,JAVASCRIPT...
    会常来看看这个BLOG,每一篇文章都是一个教程。
    em12em12em12
  • 2007-10-4 22:22:34 回复该留言
  • quote 1.草履虫
  • http://cceer.xmu.edu.cn/blog/
  • 对于问题描述我发在木耳论坛
    http://xmuer.com/viewthread.php?tid=49473
    有兴趣的可以去讨论讨论.当然也欢迎在这里留言
    lq917 于 2008-4-21 19:43:02 回复
    我看了一下上面的留言,其实应该这样:
    当我选择完3级甚至是4级以后,突然发现选择错了,或者改变主意了,要从第1级开始重选,这时应该在选择完第1级后,将后面几级全部初始化,而不是仅仅初始化后面一级。
  • 2008-4-21 19:43:02 回复该留言

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

Powered By Z-Blog 1.7 Laputa Build 70216

Copyright 2007-2008 草履虫 All Rights Reserved.