荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: sephiroth (birds of paradise), 信区: Internet
标  题: 在下拉列表中动态显示树形选项
发信站: 荔园晨风BBS站 (Tue Jun 18 11:10:52 2002), 转信


----------------------------------------------------------------
                     ----★ 警 告 ★----
    该文系原创作品。版权没有,盗版不究。欢迎复制传播,但是请保持
文章的完整性、注明作者及出处,且不得以盈利为目的。

            (C)2002  Lounge Workroom
            作者:边城浪子
-----------------------------------------------------------------


在WEB页面上使用SELECT标签提供一组选项供济览者选择是一种很普遍的做法,但
通常来说为一系列固定的选项。本人在制作某一网页时需要提供一组以树形结构存
储的非固定的选项供用户选择,经过大量试验后,我使用一个下拉列表成功地实现
了。

具体方案就是:首先,在列表框中显示一组主选项(也可能会是从一个非主选项开
始,后面介绍),当用户从中选择一项后,JavaScript代码测试所选的选项是否有
子项,若有子项则在该列表框中显示,用户可以作进一步的选择,选项中还提供返
回上一级列表的选项,以便用户错选后有机会进行反悔。在这里,用户如想选择一
个深层次的选项,可能要连续进行好几次的选择才能实现,好像有点麻烦。但对于
一个以树形结构存放的数据来说这也是最直观的了。对于一个有大量选项的选择来
说,这也是一种好的组织方法。

下面通过一个例子来谈谈具体的实现。这是一个论坛发表文章的页面的ASP代码的
一部分,在标题为文章分类的下拉列表中,显示一驵以树形结构存放在数据库中的
文章分类信息。在这里你不必一定选择最里层的选项,你可以在任何一个选项上停
止做进一步的选择,这是允许的。因为,假如你只是发表一篇对编程进行概括性描
述,而不涉及任何一种语言的文章,你只需选择程序设计这一选项就行了,尽管其
下还有Visual Studio系列、Borland系列、OpenGL技术、DirectX技术等子项。相
关代码段如下:

<%
    ...
    Response.Write "内容分类:<select id=""classlist""
onchange=""changelist();"">"
    'Add this place:the luntan's class field
    Response.Write "</select>" & vbCrLf
%>
<script language="javascript">
<!--
<%
dim aTree(),root,i,strS,s
s=1
root=CInt(bbsid)
if root=0 then root=-1
Call GetClassTree("list_lt",aTree)

call showClass(aTree,root,s,0)
strS= "var s=new Array("
for i=1 to s
    strS=strS & "s" & i & ","
next
If right(strS,1)="," Then strS=Left(strS,Len(strS)-1)
strS=strS & ");"
Response.Write strS
%>
//set the select's item
start();

function start(){
    //-------------------------
    removeall();
    addselect(s[0]);
    //return false;
    //----------=-==-=---------
}

function changelist(){
    var p=document.all.classlist;
    var id=p.selectedIndex
    var m=p.options(id).value;
    if(m<0)m=-m;
    document.all.type.value=p.options[id].text;
    if(id==0||m==0) return false;
    removeall();
    addselect(s[m-1]);
    p.blur();
    return false;
}

function removeall(){
    var o1=document.all.classlist;
    for(;o1.options(0)!==null;){
        o1.options.remove(0);
    }
}

function addselect(a){
    var o2;
    var o1=document.all.classlist;
    for(var i=0;i<a.length;i+=3)
    {
        o2=document.createElement("option");
        o2.value=a[i];
        o2.text=a[i+2];
        if (a[i]==0)
        {
            o2.style.color='#000060';
        }
        else if(a[i]>0)
        {
            o2.style.color='#ff0000';
        }
        else
        {
            o2.style.color='#0000ff';
        }
        if(i==0)
        {
            o2.style.backgroundColor='#ffffa0';
            o2.style.color='#a06020';
        }
        o1.options.add(o2);
    }
    document.all.type.value=a[2];
}
//-->
</script>
...

下面对程序进行一些解释:

代码开始的不相关部分我己省去。

程序首先用ASP输出一个下拉列表的HTML代码,ID为“classlist”。

ASP代码其后就是JavaScript代码。在JavaScript代码的开始部分又嵌入了一段
ASP代码。这里的ASP代码是用来生成列表选项的数据,并成为JavaScript代码的一
个二维数组定义语句。在这段ASP代码中,使用了两个函数,GetClassTree()函数
的功能是从数据库中读取所有的相关记录,并用来填充一个参数数组来返回数据。
该函数的第一个参数是表句,第二个参数就是用来返回数据的可变数组。该函数定
义如下:

<%
Function GetClassTree(byVal TableName,aData)
    '从数据库中读取数据
    strSQL="SELECT * FROM " & TableName & " WHERE true ORDER BY id"
    oRs.Open strSQL,oConn,1,1
    '将数据转入一个数组中
    ReDim aData(oRs.RecordCount-1,4)
    For i=0 to oRs.RecordCount-1
        aData(i,0)=oRs("id")        '记录索引号
        aData(i,1)=oRs("name")      '记录名称
        aData(i,2)=oRs("parent")    '父记录索引号
        aData(i,3)=oRs("show")      '记录的说明
        aData(i,4)=oRs("place")     '记录的参数,用它来决定记录的操作权

        oRs.MoveNext
    next
    oRs.Close
    GetClassTree=true
End Function
%>

在这个函数中,strSQL是一个全局变量,用来设定SQL语句;oRs和oConn是全局的
Recordest和Conncent对象。该函数应根据你所用的数据库不同而自己进行定义。


函数ShowClass()其实是对己读入的数据进行变换,输出JavaScript语句。它有四
个参数,第一个是数据数组,通过上面那个函数获得第二个是记录索引号,表示需
要输出该记录及其子项;第三个是一个数字。规定当前输出的JavaSciprt语句的序
号,给出该参数是因为该函数是递归的。函数返回时它就是最后一个JavaScript语
句的编号;第四个参数也是一个递归参数,调用该函数时应置为0。下面给出该函
数的定义:

<%
'###################################
'#一个使用SELECT来显示分类结构     #
'###################################
'aTree 类结构数组
's 当前类的索引
'm 当前类的编号
'parent 当前类的父类的编号
'###################################
function ShowClass(aTree,s,m,parent)
    dim strJava,n,strSonIndex,aSon,Son,iSon,cSon,p
    p=m
    n=GetArrayIndex(s,aTree)
    if n<0 then
        ShowClass=false
        Exit Function
    End If
    strJava="var s" & m & "=new Array(" & m & "," & aTree(n,0) & ",'"
& replace(aTree(n,1),"'","\'") & "',"
    strSonIndex=GetSon(s,aTree)
    aSon=Split(strSonIndex,",")
    for each Son in aSon
        iSon=CInt(Son)
        If GetSon(aTree(iSon,0),aTree)="" Then
            cSon=0
        Else
            m=m+1
            cSon=m
        End If
        strJava=strJava & cSon & "," &  aTree(iSon,0) & ",'" &
replace(aTree(iSon,1),"'","\'") & "',"
        If cSon<>0 Then
            if not showClass(aTree,aTree(iSon,0),m,p) then
                ShowClass=false
                exit function
            end if
        End If
    next
    If right(strJava,1)="," then strJava=left(strJava,Len(strJava)-1)
    If parent>0 then strJava=strJava & ",-" & parent & ",0,'▲..返回上一
级'"
    strJava=strJava & ");" & vbcrlf
    Response.Write strJava
    showClass=true
end function

%>

在该函数中又用到了几个函数,它们的定义如下:

<%
'该函数用来获取给定类的所有子类,并将结果连接成一个字符串返回
'字符串中的子索引号以逗号分割
'id:给定类的索引
'aTree:数据数组
Function GetSon(id,aTree)
    Dim strSon,i
    strSon=""
    For i=LBound(aTree) to UBound(aTree)
        If aTree(i,2)=id Then
            strSon=strSon & "," & i
        End if
    Next
    If Left(strSon,1)="," Then strSon=Mid(strSon,2)
    GetSon=strSon
End function
%>

<%
'该函数用来查找给定类在数组中的下标
'id:给定类的索引
'aTree:数据数组
Function GetArrayIndex(id,aTree)
    Dim i
    For i=LBound(aTree,1) to UBound(aTree,1)
        If aTree(i,0)=id Then
            GetArrayIndex=i
            Exit Function
        End If
    Next
    GetArrayIndex=-1
End Function
%>

好了,下面回到对主代码段的叙述。在调用函数ShowClass()输出了每个带有子项
的数据的JavaScript数组定义语句之后,函数返回的s值确定了一共产生了多少个
这样的数组。这些语句产生的数组定义有这样的规律:它们的名称从“s1”开始,
并并编号结束于返回的S值,尽管语句并非按该序号排列。如s返回20,则一共产生
20个数组,分别是:s1,s2,...,s19,s20。

然后的ASP代码根据s值生成一个数组定义语句,将这些数组组按顺序织成一个二维
数组s。这就是在客户端执行JavaScript代码所需要的全部数据。

再下面的JavaScript代码就是用来响应用户的操作,在下拉列表框中动态地,按我
们预先的规律显示出来。

JavaScript代码主要部分是三个函数:changelist()、removeall()、
addselect(a)。下面分别讨论一下这三个函数。

一、函数removeall()。该函数用于将下拉列表中原有的内容去除,以便显示新的
内容。代码很简单,就不再描述了。

二、函数addselect(a)。该函数用天将新的内容增加到下拉列表中,供用户作进一
步的选择。首先生成一个option对象,然后根据传入的数组中的当前选项内容来设
置该对象的标题和值,并根据选项的不同而设定它不同的样式。父选项带有浅黄的
底色,字符偏深橙;没有再下级子项的子选项为深蓝色表示该选项是终点;具有再
子级子项的子项的子先项是红色,表示当你选择该项后还可以再往下进行选择;返
回上级选项是浅蓝色。这样用户在作选择时就一目了然了。生成的选项使用
select对象的options集合的add方法将它加入select对象中。加入新选项是循环进
行的,直到数组中的所有内容都加入了为止。

三、函数changelist()。该函数是一个消息处理函数,它用来响应select对象的
onchange事件,当用户进行了选择之后,onchange消息被触发,于是便执行
changelist()函数。在该函数中,首先程序读取下拉列表的当前项索引,根据索引
取得所当前项的值。如果当前项具有下级选,则删除当前下拉列表的所有选项,并
死列出该项的子项供进一步选择。否则立刻返回。

在JavaScript代码开始处还调用了函数start(),该函数用来对下拉列表框进行初始
化。显示一组主选项。显示主选项很简单,就是用s[0]为参数来调用addlist()函
数。因为数组s[0]中存放的就是主选项。

附:从一个非主选项开始

上面讨论了实现的全过程。当然,根据个人情况的不同,程序肯定是要作一定的修
改的,这里就不在讨论了。下面说一下如何在页面装载时首先定位在某一个非主选
项上。我们需要对start()函数作一点修改。给该函数增加一个参数,也就是需要
在开始时显示的选项的索引。改后的start()函数如下:

function start(id){
    //-------------------------
    var i,j,pc,pr
    var fpp=1;
    removeall();
    for(i=0;(i<s.length)&&fpp;i++)
    {
        var os=s[i];
        for(j=0;(j<os.length/3)&&fpp;j++)
        {
            if(os[j*3+1]==id)
            {
                pc=i;
                pr=j;
                //fpp=0;
            }
        }
    }
    addselect(s[pc]);
    document.all.classlist.selectedIndex=pr;
    //return false;
    //----------=-==-=---------
}

函数中使用一个二重循环在数组中进行搜索指定的id,根据结果来调用
addselect()函数,而不是使用s[0]。并在函数的末尾将下拉列表的当前选项定位
至我们需要的地方。

--





※ 来源:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 192.168.44.43]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店