为解决省、市、区、区域4级级联菜单,在网上搜索了大量的级联菜单解决方案,也请教过不少朋友,要么过于复杂,要么过于占内存,未果。

在建议下,悉心读《ajax基础教程》4余遍,方有与ajax相识恨晚之感,唯一的感慨就是好用好用绝对好用。

现在把已经可以正常运行的例子的核心代码分享:
客户端ajax代码如下:
<script type="text/javascript">
        var xmlHttp;
        var domainId;
        var type;
        
        function refreshList(typesource) {
            createXMLHttpRequest();
            type 
= typesource;
            
if ("p" == type) {
                getSelectedId(
"province_select");
            } 
else if("c" == type) {
                getSelectedId(
"city_select");
            } 
else if("s" == type) {
                getSelectedId(
"section_select");
            } 
            var url 
= "enterpriseManage2.html?method=retrieve&ts=" + new Date().getTime();
            var queryStr 
= "domainId=" + domainId;
            alert(queryStr);
            xmlHttp.onreadystatechange
=handleStateChange;
            xmlHttp.open(
"POST", url);
            xmlHttp.setRequestHeader(
"Content-Type","application/x-www-form-urlencoded;");
            xmlHttp.send(queryStr);
        }
        
        function handleStateChange() {
            
if (xmlHttp.readyState == 4) {
                
if (xmlHttp.status == 200) {
                    updateList();
                }
            }
         }
         
         function getSelectedId(elementId) {
             alert(
"elementId: " + elementId);
             
//var selectedId = null;
             var options = document.getElementById(elementId).childNodes;
             var option 
= null;
             
for (var i = 0, n = options.length; i < n; i++) {
                 option 
= options[i];
                 
if (option.selected) {
                     domainId 
= option.value;
                     
//return selectedId;
                 }
             }
         }
         
         function updateList() {
             alert(
"type: " + type);
             
if ("p" == type) {
                 var select 
= document.getElementById("city_select");
                 var options 
= xmlHttp.responseXML.getElementsByTagName("domain");
                 for (var i = 0, n = options.length; i < n; i++) {
                     select.appendChild(createElementWithValue(options[i]));
                 }
             } 
else if("c" == type) {
                 var select 
= document.getElementById("section_select");
                 var options 
= xmlHttp.responseXML.getElementsByTagName("domain");
                 for (var i = 0, n = options.length; i < n; i++) {
                     select.appendChild(createElementWithValue(options[i]));
                 }    
             } 
else if("s" == type) {
                 var select 
= document.getElementById("appointDomain");
                 var options 
= xmlHttp.responseXML.getElementsByTagName("domain");
                 
for (var i = 0, n = options.length; i < n; i++) {
                     select.appendChild(createElementWithValue(options[i]));
                 }    
             }
         }

         
         function createElementWithValue(text) {
             var element 
= document.createElement("option");
             element.setAttribute(
"value", text.getAttribute("id"));
             var text 
= document.createTextNode(text.firstChild.nodeValue);
             element.appendChild(text);
             
return element;
         }
        
        function createXMLHttpRequest() {
            
if(window.XMLHttpRequest) {
                    xmlHttp = new XMLHttpRequest();
                } 
else if (window.ActiveXObject) {
                    try {
                        xmlHttp 
= new ActiveXObject("Msxml2.XMLHTTP");
                    } 
catch (e) {
                        
try {
                            xmlHttp 
= new ActiveXObject("Microsoft.XMLHTTP");
                        } 
catch (e) {
                        }
                    }
                }
        }

    
</script>


页面调用处代码如下:
<td align="left" class="list_content" width="75%">
    省
   
<select id="province_select" name="province_select" onchange="refreshList('p');"> 
        
<option value="" SELECTED>请选择</option>
            
<%
                java.util.Iterator it 
= ((java.util.List)request.getAttribute("province_options")).iterator();
               
while (it.hasNext()) {
                    Province province 
= (Province)it.next();
             
%>
             
<option value=<%=province.getId()%>><%=province.getName()%></option>
             
<%
                 }
             
%>
   
</select>
    市
   
<select id="city_select" name="city_select" onchange="refreshList('c');">      
         <option value="" SELECTED>请选择</option>
   
</select>
    区
   
<select id="section_select" name="section_select" onchange="refreshList('s');"> 
        
<option value="" SELECTED>请选择</option>
   
</select>
    区域
   
<select id="appointDomain" name="appointDomain"> 
        
<option value="" SELECTED>请选择</option>
   
</select>
</td>


服务器端action(Struts)代码如下:
 1 public ActionForward retrieve(ActionMapping mapping, ActionForm actionForm,
 2             HttpServletRequest request, HttpServletResponse response) {
 3         String domainId = request.getParameter("domainId");
 4         DomainFactory factory = DomainFactory.getInstance();
 5         Object domain = factory.getDomain(domainId);
 6         StringBuffer responseXML = new StringBuffer("<domains>");
 7         if(domain instanceof Province) {
 8             Province province = (Province)domain;
 9             Iterator it = province.getCities().iterator();
10             while (it.hasNext()) {
11                 City city = (City)it.next();
12                 responseXML.append("<domain");
13                 responseXML.append(" id='" + city.getId());
14                 responseXML.append("'>");
15                 responseXML.append(city.getName());
16                 responseXML.append("</domain>");
17             }
18         } else if(domain instanceof City) {
19             City city = (City)domain;
20             Iterator it = city.getSections().iterator();
21             while (it.hasNext()) {
22                 Section section = (Section)it.next();
23                 responseXML.append("<domain");
24                 responseXML.append(" id='" + section.getId());
25                 responseXML.append("'>");
26                 responseXML.append(section.getName());
27                 responseXML.append("</domain>");
28             }
29         } else if (domain instanceof Section) {
30             Section section = (Section)domain;
31             Iterator it = section.getRegions().iterator();
32             while (it.hasNext()) {
33                 Region region = (Region)it.next();
34                 responseXML.append("<domain");
35                 responseXML.append(" id='" + region.getId());
36                 responseXML.append("'>");
37                 responseXML.append(region.getName());
38                 responseXML.append("</domain>");
39             }
40         } 
41         responseXML.append("</domains>");
42         response.setContentType("text/xml");
43         try {
44             PrintWriter out = (PrintWriter)response.getWriter();
45             out.write(responseXML.toString());
46             System.out.println(responseXML.toString());
47             //out.flush();
48         } catch (IOException e) {
49             //do nothing
50             e.printStackTrace();
51         }
52         return null;
53     }


附注:这里用jsp或者servlet都可行。今天还看到一个朋友在dearbook上问某书的示例为啥不用Servlet而用JSP,

问题如下:读第*章,发现XMLHttpRequest.open(method,url,true)中的url请求的都是jsp,然后由jsp再调用处理方法,然后再out.print().不能直接发送请求到servlet让servlet处理再out.print()?疑惑...?

我的观点:
jsp的调用和out打印与servlet本质上是一致的;如果采用servlet从理论上更说得过去,但是对于示例未必最佳,毕竟jsp只要放在web容器的某个应用下就ok;如果是servlet则需要配置;对于一本讲述概要而不是深入讨论最佳实践的书,我觉得作者的不足是没有提到其它可行方案或者解释为啥通过这个方式来示例;对于读者来说,应该产生这个疑问,并且该弄明白为啥这么干

声明:
本例子在firefox下完全正常运行;
在IE下运行到红色标记处得到的对象的个数居然是0;严重疑惑中,希望得到朋友们的指点.....