先看看演示:在线演示 有兴趣的话,我们就看看是如何实现这样的效果的。 首先考察菜单的数据结构。在例子中,我们只用到了二级菜单,也就是说只有一层折叠。为了方便菜单的扩展,我打算将菜单内容做成 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> </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> </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”表示隐藏。 其它的就是关于菜单的样式了,使之如何美观。
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> </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> </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> 不善于写教程,只是简单介绍了一下思路,欢迎大家交流。