另外发现有人说用树的方式会更灵活和方便,如图 

花了半天时间实现一个多级联动下拉框,目的是对某一植物进行“门纲目科属”的归类。使用的技术是javascript+xml,之所以不用数据库,一来这 方面的数据虽然量大但都是固定不变的,二来不希望加重服务器的负担,第三是因为这种多级从属关系的数据不太适合放在数据库里。

这是大概的思路:
1、读取xml文件
2、当一个下拉框选中某选项时,根据该选项,当前节点指向下一层,进入下一层下拉框的设置
3、取消当前下拉框的禁用,禁用下一层的下拉框
4、清空当前下拉框的选项
5、根据当前节点读取xml的数据,设置下拉框选项
6、返回步骤2

代码:
JavaScript

1. var
2. var
3. var  currentNode; //当前所在节点
4.     
5.  setBrowserType();   
6. "classify.xml"
7.     
8. //读取xml文件数据并设置门、纲、目、科、属的下拉框
9.     
10. //设置“门”的下拉框
11. function
12.      currentNode=xmlDoc.documentElement;   
13. var
14. var
15. if (browserType== "IE"
16. for ( var
17. //从门到属,都有name属性标签,并且所有下拉框选项索引都是从1开始
18. "name"
19. new
20.          }   
21.      }   
22. else { //FF
23. //FireFox没有selectNodes()方法,且其childNodes的对应索引是1,3,5,7...
24. for ( var
25.              phylumName=phylums[i].childNodes[1].textContent;   
26. new
27.          }   
28. "disabled"
29. "disabled"
30. "disabled"
31. "disabled"
32.      }   
33.  }   
34.     
35. //设置“纲”的下拉框
36. function
37. //取消下拉框的禁用
38. //后面的下拉框禁用,这是因应各下拉框的无序选择可能产生的错误
39. //比如选了“科”又回头重新选“目”,或更改同一个下拉框选项)
40. null
41. "disabled"
42. "disabled"
43. "disabled"
44.         
45.      clearOption(document.forms[0].clazz);   
46. var
47. var
48. //将选中的门节点作为当前节点,注意这里需要将索引回减1
49. //因为门的父节点没有name属性标签,而下拉框的索引又是从1开始
50. //currentNode的赋值应使用绝对定位,也是因应各下拉框的无序选择
51. //currentNode=currentNode.childNodes(selectedIndex-1);
52. if (browserType== "IE"
53.          currentNode=xmlDoc.documentElement.childNodes(selectedIndex-1);   
54.          clazzes=currentNode.childNodes;   
55. //因为门节点的第一个子节点为name属性标签,故循环时索引从1开始
56. //相应的下拉框的索引就与纲节点的索引同步(不需要options[i+1]),目、科、属也是一样
57. for ( var
58. "name"
59. new
60.          }   
61.      }   
62. else { //FF
63.          currentNode=xmlDoc.documentElement.childNodes[selectedIndex*2-1];   
64.          clazzes=currentNode.childNodes;   
65. for ( var
66.              clazzName=clazzes[i+2].childNodes[1].textContent;   
67. new
68.          }   
69.      }   
70.         
71.         
72.  }   
73.     
74. //设置“目”的下拉框
75. function
76. //取消下拉框的禁用
77. //后面的下拉框禁用,这是因应各下拉框的无序选择可能产生的错误(比如选了“科”又回头重新选“目”)
78. null
79. "disabled"
80. "disabled"
81.         
82.      clearOption(document.forms[0].order);   
83. var
84. //currentNode的赋值应使用绝对定位
85. var  phylumSI=document.forms[0].phylum.selectedIndex;     //phylum selected index
86. if (browserType== "IE"
87.          currentNode=xmlDoc.documentElement   
88.              .childNodes[phylumSI-1]   
89.              .childNodes[selectedIndex];   
90. var
91. for ( var
92. "name"
93. new
94.          }   
95. else
96.          currentNode=xmlDoc.documentElement   
97.              .childNodes[phylumSI*2-1]   
98.              .childNodes[selectedIndex*2+1];   
99. var
100. for ( var
101.              orderName=orders[i+2].childNodes[1].textContent;   
102. new
103.          }   
104.      }   
105.  }   
106.     
107. //设置“科”的下拉框
108. function
109. null ; //取消下拉框的禁用
110. "disabled" ; //后面的下拉框禁用
111.         
112. //currentNode的赋值应使用绝对定位
113. var  phylumSI=document.forms[0].phylum.selectedIndex; //phylum selected index
114. var  clazzSI=document.forms[0].clazz.selectedIndex;   //clazz selected index
115.      clearOption(document.forms[0].family);   
116. var
117. var
118. if (browserType== "IE"
119.          currentNode=xmlDoc.documentElement   
120.              .childNodes[phylumSI-1]   
121.              .childNodes[clazzSI]   
122.              .childNodes[selectedIndex];   
123.          families=currentNode.childNodes;   
124. for ( var
125. "name"
126. new
127.          }   
128.      }   
129. else
130.          currentNode=xmlDoc.documentElement   
131.              .childNodes[phylumSI*2-1]   
132.              .childNodes[clazzSI*2+1]   
133.              .childNodes[selectedIndex*2+1];   
134.          families=currentNode.childNodes;   
135. for ( var
136.              familyName=families[i+2].childNodes[1].textContent;   
137. new
138.          }   
139.      }   
140.  }   
141.     
142. //设置“属”的下拉框
143. function
144. null ; //取消下拉框的禁用
145.         
146. //currentNode的赋值应使用绝对定位
147. var  phylumSI=document.forms[0].phylum.selectedIndex; //phylum selected index
148. var  clazzSI=document.forms[0].clazz.selectedIndex;   //clazz selected index
149. var  orderSI=document.forms[0].order.selectedIndex;   //order selected index
150.      clearOption(document.forms[0].genus);   
151. var
152. var
153.         
154. if (browserType== "IE"
155.             
156.          currentNode=xmlDoc.documentElement   
157.              .childNodes(phylumSI-1)   
158.              .childNodes(clazzSI)   
159.              .childNodes(orderSI)   
160.              .childNodes(selectedIndex);   
161.          genuses=currentNode.childNodes;   
162. for ( var
163. //属为叶节点
164. var
165. new
166.          }   
167.      }   
168. else
169.          currentNode=xmlDoc.documentElement   
170.              .childNodes[phylumSI*2-1]   
171.              .childNodes[clazzSI*2+1]   
172.              .childNodes[orderSI*2+1]   
173.              .childNodes[selectedIndex*2+1];   
174.          genuses=currentNode.childNodes;   
175. for ( var
176. //属为叶节点
177. var
178. new
179.          }   
180.      }   
181.  }   
182.     
183. //清空下拉框选项
184. function
185. for ( var
186. null
187.      }   
188.  }   
189.     
190. //判断浏览器类型
191. function
192. if  (window.ActiveXObject){ //IE
193. "IE"
194. else
195. "FireFox"
196.      }   
197.  }   
198.     
199. //载入xml
200. function
201. if  (browserType== "IE" ){ //IE
202. new  ActiveXObject( "Microsoft.XMLDOM"
203. false
204.          xmlDoc.load(xmlName);   
205. else
206. //      xmlDoc=document.implementation.createDocument("", "", null);
207. //      xmlDoc.async = false;
208. //      xmlDoc.load("classify.xml");
209. "FireFox"
210. var  xmlHttp =  new
211. "GET" ,  "classify.xml" ,  false
212. null
213.          xmlDoc=xmlHttp.responseXML;   
214.             
215. //FireFox没有selectNodes()方法,且xml中,其childNodes的对应索引是1,3,5,7...
216. //      alert(xmlDoc.getElementsByTagName("phylum")[1]
217. //          .childNodes[3].childNodes[3].childNodes[1].textContent);
218.      }   
219.  }

最后是xml文件的内容

1. <? xml   version = "1.0"   encoding = "UTF-8" ?>
2. < plant >
3. < phylum >
4. < name > 被子植物门 </ name >
5. < clazz >
6. < name > 双子叶植物纲 </ name >
7. < order >
8. < name > 菊目 </ name >
9. < family >
10. < name > 菊科 </ name >
11. < genus > 菊属 </ genus >
12. </ family >
13. < family >
14. < name > 桔梗科 </ name >
15. < genus > 同钟花属 </ genus >
16. < genus > 刺萼参属 </ genus >
17. </ family >
18. </ order >
19. < order >
20. < name > 胡椒目 </ name >
21. < family >
22. < name > 胡椒科 </ name >
23. < genus > 胡椒属 </ genus >
24. < genus > 草胡椒属 </ genus >
25. < genus > 齐头绒属 </ genus >
26. </ family >
27. </ order >
28. </ clazz >
29. </ phylum >
30. < phylum >
31. < name > 蕨类植物门 </ name >
32. < clazz >
33. < name > 石松纲 </ name >
34. < order >
35. < name > 石松目 </ name >
36. < family >
37. < name > 石松科 </ name >
38. < genus > 石松属 </ genus >
39. </ family >
40. </ order >
41. </ clazz >
42. </ phylum >
43. </ 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]。