1. 论坛系统升级为Xenforo,欢迎大家测试!
    排除公告

利用了一点 AJax 知识的折叠菜单

本帖由 不学无术2005-09-27 发布。版面名称:前端开发

  1. 不学无术

    不学无术 Ulysses 的元神

    注册:
    2005-08-31
    帖子:
    16,714
    赞:
    39
    先看看演示:在线演示

    有兴趣的话,我们就看看是如何实现这样的效果的。

    首先考察菜单的数据结构。在例子中,我们只用到了二级菜单,也就是说只有一层折叠。为了方便菜单的扩展,我打算将菜单内容做成 xml 文件。

    xml 文件(menus.xml)结构本身就很清晰,基本上能以原样呈现菜单层次,代码如下:

    代码:
    <?xml version="1.0" encoding="gb2312"?>
    <channels>
      <channel>
        <title>系统设置</title>
    	<menu>
    	  <title>系统参数</title>
    	  <href>#</href>
    	</menu>
    	<menu>
    	  <title>系统菜单</title>
    	  <href>#</href>
    	</menu>
    	<menu>
    	  <title>系统参数</title>
    	  <href>#</href>
    	</menu>
    	<menu>
    	  <title>系统参数</title>
    	  <href>#</href>
    	</menu>
      </channel>
      <channel>
        <title>会员管理</title>
    	<menu>
    	  <title>会员列表</title>
    	  <href>#</href>
    	</menu>
    	<menu>
    	  <title>会员列表</title>
    	  <href>#</href>
    	</menu>
    	<menu>
    	  <title>添加会员</title>
    	  <href>#</href>
    	</menu>
    	<menu>
    	  <title>系统参数</title>
    	  <href>#</href>
    	</menu>
      </channel>
      <channel>
        <title>图片管理</title>
    	<menu>
    	  <title>图片列表</title>
    	  <href>#</href>
    	</menu>
    	<menu>
    	  <title>系统参数</title>
    	  <href>#</href>
    	</menu>
    	<menu>
    	  <title>系统参数</title>
    	  <href>#</href>
    	</menu>
    	<menu>
    	  <title>系统参数</title>
    	  <href>#</href>
    	</menu>
      </channel>
      <channel>
        <title>系统设置</title>
    	<menu>
    	  <title>系统参数</title>
    	  <href>#</href>
    	</menu>
    	<menu>
    	  <title>系统参数</title>
    	  <href>#</href>
    	</menu>
    	<menu>
    	  <title>系统参数</title>
    	  <href>#</href>
    	</menu>
    	<menu>
    	  <title>系统参数</title>
    	  <href>#</href>
    	</menu>
      </channel>
      <channel>
        <title>系统设置</title>
    	<menu>
    	  <title>系统参数</title>
    	  <href>#</href>
    	</menu>
    	<menu>
    	  <title>系统参数</title>
    	  <href>#</href>
    	</menu>
    	<menu>
    	  <title>系统参数</title>
    	  <href>#</href>
    	</menu>
    	<menu>
    	  <title>系统参数</title>
    	  <href>#</href>
    	</menu>
      </channel>
    </channels>
    其中每个 channel 节点对应一个折叠菜单,每个 menu 对应一个子菜单项。title 节点是要显示的 链接文本,href 是链接路径。当然根据需要还可以加上能表示 a 元素的其它属性的节点。

    在 HTML 页面中我们需要加在该 xml 来生成菜单对应的 HTML 代码。加载 XML 文件的方式有很多种,但是鉴于我正好在学习 AJAX 应用,我就用了这个。

    在 IE 中,XMLHttp 是通过 activeX 组件来实现的,这和 Mozilla 系列的不一样。通过下面部分代码,可以处理好浏览器的兼容性:

    代码:
        var xmlhttp = false;
    	
        /*@cc_on @*/
        /*@if (@_jscript_version >= 5)
        // JScript gives us Conditional compilation, we can cope with old IE versions.
        // and security blocked creation of the objects.
        try {
            xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
        }
        catch (e) {
            try {
                xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
            }
        	catch (E) {
                xmlhttp = false;
            }
        }
        @end @*/
        if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
            xmlhttp = new XMLHttpRequest();
        }
    后面就是利用 XMLHttpRequest 来加载 XML 文档,并对返回的 XML 内容进行分析,以生成对应的 HTML 代码:

    代码:
    	xmlhttp.open("GET", "menus.xml", true);
    	xmlhttp.onreadystatechange = function() {
    	    if (xmlhttp.readyState == 4) {
    		    var s = "";
    			var objXml = xmlhttp.responseXML;
    			var objDocument = objXml.documentElement;
    			var arrChannels = objDocument.childNodes;
    			var arrChannels = objXml.getElementsByTagName("channel");
    			var objChannel;
    			var objMenu;
    			for (var i = 0; i < arrChannels.length; i++) {
    			    objChannel = arrChannels;
    				if (objChannel.childNodes[0].nodeType == 3) {  // 判断第一个节点类型
    				    s = s + "<div class=\"menuTitle\" onClick=\"showHideMenu(this);\"><span>&nbsp;</span>" + objChannel.childNodes[1].childNodes[0].nodeValue + "</div>";
    				    s = s + "<div class=\"menuContent\"><ul>";
    					for (var k = 1; k < Math.floor((objChannel.childNodes.length / 2)); k++) {
    					    objMenu = objChannel.childNodes[2 * k + 1];
    					    s = s + "<li><a href=\"" + objMenu.childNodes[3].childNodes[0].nodeValue + "\" title=\"" + objMenu.childNodes[1].childNodes[0].nodeValue + "\" target=\"mainFrame\">" + objMenu.childNodes[1].childNodes[0].nodeValue + "</a></li>";
    					}
    				    s = s + "</ul></div>";
    				}
    				else {  // IE
    				    s = s + "<div class=\"menuTitle\" onClick=\"showHideMenu(this);\"><span>&nbsp;</span>" + objChannel.childNodes[0].childNodes[0].nodeValue + "</div>";
    				    s = s + "<div class=\"menuContent\"><ul>";
    				    for (var k = 1; k < objChannel.childNodes.length; k++) {
    					    objMenu = objChannel.childNodes[k];
    				        s = s + "<li><a href=\"" + objMenu.childNodes[1].childNodes[0].nodeValue + "\" title=\"" + objMenu.childNodes[0].childNodes[0].nodeValue + "\" target=\"mainFrame\">" + objMenu.childNodes[0].childNodes[0].nodeValue + "</a></li>";
    				    }
    				    s = s + "</ul></div>";
    				}
    			}
    			document.getElementById("menus").innerHTML = s;
    		}
    	}
    	xmlhttp.send(null);


    在这个应用中我们还是需要注意浏览器的区别。因为对于上面格式化的 XML 文档,不同的节点间有一个空格,IE 中会忽略,但是其它大多数浏览器会将其作为一个文本类型的节点来处理。

    好了,结构生成了,现在就是如何操作菜单的显示与隐藏。

    代码:
    function showHideMenu(o) {
        var objTitle = o;
    	// 有一个文本节点,可能是换行符
    	// IE 下面的效果有些问题
    	// responseXML 获得的代码中不包含这个换行
    	//var objContent
    	//if (document.all) {
    	    objContent = objTitle.nextSibling;
    	//}
    	//else {
    	    //objContent = objTitle.nextSibling.nextSibling;
    	//}
    	var objSpan = objTitle.childNodes[0];
    	if (objContent.style.display != "block") {
    		objContent.style.display = "block";
    		objSpan.className = "arrowOpen"
    	}
    	else {
    	    objContent.style.display = "none";
    		objSpan.className = "arrowClose"
    	}
    }
    这里利用了一点 DOM 知识。当我们点击菜单标题的时候,我们就驱动上面的程序。o 代表执行了点击操作的菜单标题 <div> ,然后可以通过 nextSibling 来获得下一个节点,即掩藏或者显示的菜单区域 <div> ,这样我们就可以通过样式表中的 display 来控制菜单的显隐了。“block”表示显示,“none”表示隐藏。

    其它的就是关于菜单的样式了,使之如何美观。
     
  2. 不学无术

    不学无术 Ulysses 的元神

    注册:
    2005-08-31
    帖子:
    16,714
    赞:
    39
    HTML 文件(left.html)的全部代码(包括 CSS / JavaScript)如下:

    代码:
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>折叠菜单</title>
    <style type="text/css">
    <!--
    body {
    	background: #FFFFFF;
    	margin: 0px;
    	padding: 0px;
    }
    body, table, td, th, tr, input, button, select, textarea, div, span {
    	font: 12px "宋体", sans-serif;
    }
    
    /* ---- 左栏 ---- */
    .leftFrame {
        background: #C3CDDE;
    }
    #menus {
        width: 160px;
    }
    .menuTitle {
        font-size: 12px;
        /*line-height: 20px;*/
    	background: url(images/title_background.gif) no-repeat left center;
    	text-align: left;
    	/*height: 20px;*/
    	padding: 4px 0 2px 14px;
    	cursor: default;
    }
    .menuTitle span {
        background: url(images/title_arrow_close.gif) no-repeat center center;
    	width: 9px;
    	height: 12px;
    	margin-right: 3px;
    	float: left;
    	cursor: default;
    }
    .menuTitle span.arrowOpen {
        background: url(images/title_arrow_open.gif) no-repeat center center;
    }
    .menuTitle span.arrowClose {
        background: url(images/title_arrow_close.gif) no-repeat center center;
    }
    .menuContent {
        font-size: 12px;
    	line-height: 150%;
    	background: #FFFFFF;
    	display: none;
    	padding: 10px;
    	overflow: auto;
    }
    .menuContent ul {
        padding: 0;
    	margin: 0;
    	list-style: none;
    }
    .menuContent ul li {
        background: url(images/dot.gif) no-repeat left center;
    	padding-left: 12px;
    }
    .menuContent a {
        color: #000000;
    	text-decoration: none;
    }
    .menuContent a:hover {
        color: #3030C0;
    	text-decoration: none;
    }
    p.copy {
        font-size: 12px;
    	line-height: 150%;
    	background: #DEE3ED;
    	padding: 10px;
    	border-top: 1px solid #626B7B;
    	border-bottom: 1px solid #626B7B;
    }
    
    /* ---- 左栏 ---- */
    -->
    </style>
    <script language="javascript" type="text/javascript">
    <!--
    function showHideMenu(o) {
        var objTitle = o;
    	// 有一个文本节点,可能是换行符
    	// IE 下面的效果有些问题
    	// responseXML 获得的代码中不包含这个换行
    	//var objContent
    	//if (document.all) {
    	    objContent = objTitle.nextSibling;
    	//}
    	//else {
    	    //objContent = objTitle.nextSibling.nextSibling;
    	//}
    	var objSpan = objTitle.childNodes[0];
    	if (objContent.style.display != "block") {
    		objContent.style.display = "block";
    		objSpan.className = "arrowOpen"
    	}
    	else {
    	    objContent.style.display = "none";
    		objSpan.className = "arrowClose"
    	}
    }
    
    function setMenus() {
        var xmlhttp = false;
    	
        /*@cc_on @*/
        /*@if (@_jscript_version >= 5)
        // JScript gives us Conditional compilation, we can cope with old IE versions.
        // and security blocked creation of the objects.
        try {
            xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
        }
        catch (e) {
            try {
                xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
            }
        	catch (E) {
                xmlhttp = false;
            }
        }
        @end @*/
        if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
            xmlhttp = new XMLHttpRequest();
        }
    	
    	
    	xmlhttp.open("GET", "menus.xml", true);
    	xmlhttp.onreadystatechange = function() {
    	    if (xmlhttp.readyState == 4) {
    		    var s = "";
    			var objXml = xmlhttp.responseXML;
    			var objDocument = objXml.documentElement;
    			var arrChannels = objDocument.childNodes;
    			var arrChannels = objXml.getElementsByTagName("channel");
    			var objChannel;
    			var objMenu;
    			for (var i = 0; i < arrChannels.length; i++) {
    			    objChannel = arrChannels;
    				if (objChannel.childNodes[0].nodeType == 3) {  // 判断第一个节点类型
    				    s = s + "<div class=\"menuTitle\" onClick=\"showHideMenu(this);\"><span>&nbsp;</span>" + objChannel.childNodes[1].childNodes[0].nodeValue + "</div>";
    				    s = s + "<div class=\"menuContent\"><ul>";
    					for (var k = 1; k < Math.floor((objChannel.childNodes.length / 2)); k++) {
    					    objMenu = objChannel.childNodes[2 * k + 1];
    					    s = s + "<li><a href=\"" + objMenu.childNodes[3].childNodes[0].nodeValue + "\" title=\"" + objMenu.childNodes[1].childNodes[0].nodeValue + "\" target=\"mainFrame\">" + objMenu.childNodes[1].childNodes[0].nodeValue + "</a></li>";
    					}
    				    s = s + "</ul></div>";
    				}
    				else {  // IE
    				    s = s + "<div class=\"menuTitle\" onClick=\"showHideMenu(this);\"><span>&nbsp;</span>" + objChannel.childNodes[0].childNodes[0].nodeValue + "</div>";
    				    s = s + "<div class=\"menuContent\"><ul>";
    				    for (var k = 1; k < objChannel.childNodes.length; k++) {
    					    objMenu = objChannel.childNodes[k];
    				        s = s + "<li><a href=\"" + objMenu.childNodes[1].childNodes[0].nodeValue + "\" title=\"" + objMenu.childNodes[0].childNodes[0].nodeValue + "\" target=\"mainFrame\">" + objMenu.childNodes[0].childNodes[0].nodeValue + "</a></li>";
    				    }
    				    s = s + "</ul></div>";
    				}
    			}
    			document.getElementById("menus").innerHTML = s;
    		}
    	}
    	xmlhttp.send(null);
    }
    -->
    </script>
    </head>
    
    <body class="leftFrame" onLoad="setMenus();">
    <div id="menus"></div>
    <p class="copy">
      版面设计:不学无术
      <br>
    
      程序制作:不学无术
      <br>
      版权所有:<a href="http://www.onlyidc.com/" title="访问 趋势科技" target="_blank">趋势科技</a>
    </p>
    </body>
    </html>
    




    不善于写教程,只是简单介绍了一下思路,欢迎大家交流。
     
  3. 老林

    老林 New Member

    注册:
    2005-09-06
    帖子:
    10,580
    赞:
    36
    支持一下
     
  4. 小叶

    小叶 New Member

    注册:
    2005-09-04
    帖子:
    17,941
    赞:
    33
    太服你了..每天你在我背后坐着不知捣鼓什么,,,
    出来这么牛的玩意.
     
  5. wm_chief

    wm_chief New Member

    注册:
    2005-09-05
    帖子:
    17,890
    赞:
    46
    不错的东东
     
  6. yeshou

    yeshou New Member

    注册:
    2005-09-06
    帖子:
    7,067
    赞:
    34
    太长了。。。
     
  7. 不学无术

    不学无术 Ulysses 的元神

    注册:
    2005-08-31
    帖子:
    16,714
    赞:
    39
    现学现卖,让您见笑了!

    以后还请多多指教呢:)
     
  8. chandler

    chandler New Member

    注册:
    2005-09-27
    帖子:
    1,921
    赞:
    1
    JJ?:eek: