另外发现有人说用树的方式会更灵活和方便,如图
花了半天时间实现一个多级联动下拉框,目的是对某一植物进行“门纲目科属”的归类。使用的技术是javascript+xml,之所以不用数据库,一来这 方面的数据虽然量大但都是固定不变的,二来不希望加重服务器的负担,第三是因为这种多级从属关系的数据不太适合放在数据库里。
这是大概的思路:
1、读取xml文件
2、当一个下拉框选中某选项时,根据该选项,当前节点指向下一层,进入下一层下拉框的设置
3、取消当前下拉框的禁用,禁用下一层的下拉框
4、清空当前下拉框的选项
5、根据当前节点读取xml的数据,设置下拉框选项
6、返回步骤2
代码:
JavaScript
- var xmlDoc;
- var browserType;
- var currentNode; //当前所在节点
- setBrowserType();
- loadXml( "classify.xml" );
- //读取xml文件数据并设置门、纲、目、科、属的下拉框
- //设置“门”的下拉框
- function setPhylum(){
- currentNode=xmlDoc.documentElement;
- var phylums=xmlDoc.documentElement.childNodes;
- var phylumName;
- if (browserType== "IE" ){
- for ( var i=0;i<phylums.length;i++){
- //从门到属,都有name属性标签,并且所有下拉框选项索引都是从1开始
- phylumName=phylums[i].selectNodes( "name" )[0].text;
- document.forms[0].phylum.options[i+1]= new Option(phylumName,phylumName);
- }
- }
- else { //FF
- //FireFox没有selectNodes()方法,且其childNodes的对应索引是1,3,5,7...
- for ( var i=1;i<phylums.length;i=i+2){
- phylumName=phylums[i].childNodes[1].textContent;
- document.forms[0].phylum.options[(i+1)/2]= new Option(phylumName,phylumName);
- }
- document.forms[0].clazz.disabled= "disabled" ;
- document.forms[0].order.disabled= "disabled" ;
- document.forms[0].family.disabled= "disabled" ;
- document.forms[0].genus.disabled= "disabled" ;
- }
- }
- //设置“纲”的下拉框
- function setClazz(selectedIndex){
- //取消下拉框的禁用
- //后面的下拉框禁用,这是因应各下拉框的无序选择可能产生的错误
- //比如选了“科”又回头重新选“目”,或更改同一个下拉框选项)
- document.forms[0].clazz.disabled= null ;
- document.forms[0].order.disabled= "disabled" ;
- document.forms[0].family.disabled= "disabled" ;
- document.forms[0].genus.disabled= "disabled" ;
- clearOption(document.forms[0].clazz);
- var clazzes;
- var clazzName;
- //将选中的门节点作为当前节点,注意这里需要将索引回减1
- //因为门的父节点没有name属性标签,而下拉框的索引又是从1开始
- //currentNode的赋值应使用绝对定位,也是因应各下拉框的无序选择
- //currentNode=currentNode.childNodes(selectedIndex-1);
- if (browserType== "IE" ){
- currentNode=xmlDoc.documentElement.childNodes(selectedIndex-1);
- clazzes=currentNode.childNodes;
- //因为门节点的第一个子节点为name属性标签,故循环时索引从1开始
- //相应的下拉框的索引就与纲节点的索引同步(不需要options[i+1]),目、科、属也是一样
- for ( var i=1;i<clazzes.length;i++){
- clazzName=clazzes[i].selectNodes( "name" )[0].text;
- document.forms[0].clazz.options[i]= new Option(clazzName,clazzName);
- }
- }
- else { //FF
- currentNode=xmlDoc.documentElement.childNodes[selectedIndex*2-1];
- clazzes=currentNode.childNodes;
- for ( var i=1;i<clazzes.length-2;i=i+2){
- clazzName=clazzes[i+2].childNodes[1].textContent;
- document.forms[0].clazz.options[(i+1)/2]= new Option(clazzName,clazzName);
- }
- }
- }
- //设置“目”的下拉框
- function setOrder(selectedIndex){
- //取消下拉框的禁用
- //后面的下拉框禁用,这是因应各下拉框的无序选择可能产生的错误(比如选了“科”又回头重新选“目”)
- document.forms[0].order.disabled= null ;
- document.forms[0].family.disabled= "disabled" ;
- document.forms[0].genus.disabled= "disabled" ;
- clearOption(document.forms[0].order);
- var orderName;
- //currentNode的赋值应使用绝对定位
- var phylumSI=document.forms[0].phylum.selectedIndex; //phylum selected index
- if (browserType== "IE" ){
- currentNode=xmlDoc.documentElement
- .childNodes[phylumSI-1]
- .childNodes[selectedIndex];
- var orders=currentNode.childNodes;
- for ( var i=1;i<orders.length;i++){
- orderName=orders[i].selectNodes( "name" )[0].text;
- document.forms[0].order.options[i]= new Option(orderName,orderName);
- }
- } else {
- currentNode=xmlDoc.documentElement
- .childNodes[phylumSI*2-1]
- .childNodes[selectedIndex*2+1];
- var orders=currentNode.childNodes;
- for ( var i=1;i<orders.length-2;i=i+2){
- orderName=orders[i+2].childNodes[1].textContent;
- document.forms[0].order.options[(i+1)/2]= new Option(orderName,orderName);
- }
- }
- }
- //设置“科”的下拉框
- function setFamily(selectedIndex){
- document.forms[0].family.disabled= null ; //取消下拉框的禁用
- document.forms[0].genus.disabled= "disabled" ; //后面的下拉框禁用
- //currentNode的赋值应使用绝对定位
- var phylumSI=document.forms[0].phylum.selectedIndex; //phylum selected index
- var clazzSI=document.forms[0].clazz.selectedIndex; //clazz selected index
- clearOption(document.forms[0].family);
- var families;
- var familyName;
- if (browserType== "IE" ){
- currentNode=xmlDoc.documentElement
- .childNodes[phylumSI-1]
- .childNodes[clazzSI]
- .childNodes[selectedIndex];
- families=currentNode.childNodes;
- for ( var i=1;i<families.length;i++){
- familyName=families[i].selectNodes( "name" )[0].text;
- document.forms[0].family.options[i]= new Option(familyName,familyName);
- }
- }
- else {
- currentNode=xmlDoc.documentElement
- .childNodes[phylumSI*2-1]
- .childNodes[clazzSI*2+1]
- .childNodes[selectedIndex*2+1];
- families=currentNode.childNodes;
- for ( var i=1;i<families.length-2;i=i+2){
- familyName=families[i+2].childNodes[1].textContent;
- document.forms[0].family.options[(i+1)/2]= new Option(familyName,familyName);
- }
- }
- }
- //设置“属”的下拉框
- function setGenus(selectedIndex){
- document.forms[0].genus.disabled= null ; //取消下拉框的禁用
- //currentNode的赋值应使用绝对定位
- var phylumSI=document.forms[0].phylum.selectedIndex; //phylum selected index
- var clazzSI=document.forms[0].clazz.selectedIndex; //clazz selected index
- var orderSI=document.forms[0].order.selectedIndex; //order selected index
- clearOption(document.forms[0].genus);
- var genues;
- var genusName;
- if (browserType== "IE" ){
- currentNode=xmlDoc.documentElement
- .childNodes(phylumSI-1)
- .childNodes(clazzSI)
- .childNodes(orderSI)
- .childNodes(selectedIndex);
- genuses=currentNode.childNodes;
- for ( var i=1;i<genuses.length;i++){
- //属为叶节点
- var genusName=genuses[i].text;
- document.forms[0].genus.options[i]= new Option(genusName,genusName);
- }
- }
- else {
- currentNode=xmlDoc.documentElement
- .childNodes[phylumSI*2-1]
- .childNodes[clazzSI*2+1]
- .childNodes[orderSI*2+1]
- .childNodes[selectedIndex*2+1];
- genuses=currentNode.childNodes;
- for ( var i=1;i<genuses.length-2;i=i+2){
- //属为叶节点
- var genusName=genuses[i+2].textContent;
- document.forms[0].genus.options[(i+1)/2]= new Option(genusName,genusName);
- }
- }
- }
- //清空下拉框选项
- function clearOption(selectElement){
- for ( var i=1;i<selectElement.options.length;i++){
- selectElement.options[i]= null ;
- }
- }
- //判断浏览器类型
- function setBrowserType(){
- if (window.ActiveXObject){ //IE
- browserType= "IE" ;
- } else {
- browserType= "FireFox" ;
- }
- }
- //载入xml
- function loadXml(xmlName){
- if (browserType== "IE" ){ //IE
- xmlDoc = new ActiveXObject( "Microsoft.XMLDOM" );
- xmlDoc.async = false ;
- xmlDoc.load(xmlName);
- } else {
- // xmlDoc=document.implementation.createDocument("", "", null);
- // xmlDoc.async = false;
- // xmlDoc.load("classify.xml");
- browserType= "FireFox" ;
- var xmlHttp = new XMLHttpRequest();
- xmlHttp.open( "GET" , "classify.xml" , false ) ;
- xmlHttp.send( null ) ;
- xmlDoc=xmlHttp.responseXML;
- //FireFox没有selectNodes()方法,且xml中,其childNodes的对应索引是1,3,5,7...
- // alert(xmlDoc.getElementsByTagName("phylum")[1]
- // .childNodes[3].childNodes[3].childNodes[1].textContent);
- }
- }
最后是xml文件的内容
- <? xml version = "1.0" encoding = "UTF-8" ?>
- < plant >
- < phylum >
- < name > 被子植物门 </ name >
- < clazz >
- < name > 双子叶植物纲 </ name >
- < order >
- < name > 菊目 </ name >
- < family >
- < name > 菊科 </ name >
- < genus > 菊属 </ genus >
- </ family >
- < family >
- < name > 桔梗科 </ name >
- < genus > 同钟花属 </ genus >
- < genus > 刺萼参属 </ genus >
- </ family >
- </ order >
- < order >
- < name > 胡椒目 </ name >
- < family >
- < name > 胡椒科 </ name >
- < genus > 胡椒属 </ genus >
- < genus > 草胡椒属 </ genus >
- < genus > 齐头绒属 </ genus >
- </ family >
- </ order >
- </ clazz >
- </ phylum >
- < phylum >
- < name > 蕨类植物门 </ name >
- < clazz >
- < name > 石松纲 </ name >
- < order >
- < name > 石松目 </ name >
- < family >
- < name > 石松科 </ name >
- < genus > 石松属 </ genus >
- </ family >
- </ order >
- </ clazz >
- </ phylum >
- </ plant >
这是部分效果图:
可以实现上下级下拉框的联动,支持无序选择,若向上重新选择,下下层下拉框将自动被禁用,下层下拉框选项也会相应改变。
有一点不足是,因为数据量实在太大,这样5个下拉框仍然可能出现某下拉框有几百甚至几千个选项,此时就失去了下拉框的意义,因此正在考虑是否应该做成输入框的形式,或者像搜索引擎那样带有输入提示功能,研究中,欢迎拍砖。
PS:重新修改了一下,可以支持FireFox了,这可真是麻烦的工程:FireFox的JavaScript的Element对象中没有 selectNodes()方法,只有调用childNodes()或者getElementsByTagName();并且在FireFox中,xml 中节点对应childNodes()的索引是1,3,5,7...,也就是说,如果你想读取xml某个节点下的第i个子节点,正常我们就会写 someNode.childNodes[i-1],但在FireFox就必须写作someNode.childNodes[i*2-1]。
另外在使用数组时,IE允许把小括号当成中括号使用(即someArray[i]和someArray(i)均合法),FireFox则不行,所以最好统一写someArray[i]。