WordPress 多级导航菜单
多级导航菜单, 是指菜单存在多个层次, 层层嵌套, 当鼠标移动到某个菜单时, 如果其包含子菜单则将相应的子菜单显示出来. 本文将提供此功能在 WordPress 的实现方法, 一般的 HTML 页面和其他程序也可以加工套用.
时隔 9 个多月, 关于导航菜单的话题又回来了. 上次写到三级菜单就不写了是因为我发现自己根本用不上, 就没去研究. 最近我在做一个小玩意儿用到了这个, 所以把它给做了出来并集成到 iNove 主题上. 因为内容繁多, 所以还是以之前的文章和代码作为基础来展开讲解, 希望这个文章会对大家有所帮助.
为了简化处理, 明确目标, 这次我们会以二级导航菜单作为原型进行扩展. 本文只对多级菜单相关处理进行讨论, 其他内容请参考以前的几篇关于菜单导航的文章, 文章链接你可以在本文相关话题中找到.
相关话题:
WordPress 导航菜单
二级导航菜单
淡出淡入导航菜单
滚动导航菜单
jQuery 导航菜单
多级导航菜单
点选式导航菜单 (待定话题)
作业分析:
因为菜单由本来的二级菜单变成了多级菜单, 所以菜单结构会有些变化 (会有新的更深层的子菜单加入). 另外, 页面的样式和脚本都会有相应的变更, 重点在于对 JavaScript 的加工.
主要操作如下:
1. 鼠标移动到一级菜单的菜单项时, 如果该菜单项包含了二级菜单, 那么在下方显示其子菜单.
2. 鼠标移动到 N (N >= 2) 级菜单的菜单项时, 如果该菜单项包含了 N+1 级菜单, 那么在右侧显示其子菜单.
预想结构:
<div id="menubar">
<ul class="menus">
<li class="..."><a href="http://.../">Home</a></li>
<li class="...">
<a href="http://.../">菜单1</a>
<ul class="children">
<li class="..."><a href="http://.../">菜单11</a></li>
<li class="...">
<a href="http://.../">菜单12</a>
<ul class="children">
<li class="..."><a href="http://.../">菜单121</a></li>
<li class="..."><a href="http://.../">菜单122</a></li>
<li class="..."><a href="http://.../">菜单123</a></li>
<li class="..."><a href="http://.../">菜单124</a></li>
</ul>
</li>
<li class="...">
<a href="http://.../">菜单13</a>
<ul class="children">
<li class="..."><a href="http://.../">菜单131</a></li>
<li class="..."><a href="http://.../">菜单132</a></li>
</ul>
</li>
</ul>
</li>
<li class="...">
<a href="http://.../">菜单2</a>
<ul class="children">
<li class="..."><a href="http://.../">菜单21</a></li>
</ul>
</li>
<li class="...">
<a href="http://.../">菜单3</a>
<ul class="children">
<li class="..."><a href="http://.../">菜单31</a></li>
<li class="...">
<a href="http://.../">菜单32</a>
<ul class="children">
<li class="..."><a href="http://.../">菜单321</a></li>
<li class="..."><a href="http://.../">菜单322</a></li>
</ul>
</li>
</ul>
</li>
...
</ul>
</div>
实施操作:
1. 调出无限级菜单 (子分类)
是否还记得如何设定列表的深度? 可以通过参数 depth 来进行设定. 当我们将深度设为 1 时不显示子分类, 将深度设为 2 时显示二级菜单. 现在我们要无限制层数的菜单, 所以可以去除这个参数, 系统会自动返回所有菜单. 所以显示菜单的代码如下:
<?php wp_list_pages('depth=2&title_li=0&sort_column=menu_order'); ?>
2. 修改菜单的样式
在子菜单项添加 display:inline;
以免在 IE (IE6/IE7/IE8) 中发生错位.
/* 子菜单的菜单项 */
#menubar ul.children li {
float:none; /* 垂直排列 */
margin:0;
padding:0;
/* multi 2009/06/11 ADD START */
display:inline; /* 对 IE 来说十分很重要 */
/* multi 2009/06/11 ADD END */
}
追加包含子菜单的菜单项的样式
#menubar ul.children li a.subtitle {
border-right:3px solid #4281B7;
width:97px;
}
添加当前菜单的效果, 以明确当前路径.
#menubar ul.menus li a:hover,
/* multi 2009/06/11 ADD START */
#menubar ul.menus li a.current {
/* multi 2009/06/11 ADD END */
background:#4281B7; /* 背景颜色 */
}
3. 加载菜单
加载菜单是应该分开处理, 对于对层次的菜单, 菜单显示有两种状态. (1) 作为二级菜单显示在上一级菜单的下方; (2) 而三级和以上层次的菜单则会显示在上一级菜单的右侧.
// 找到所有的菜单
var menus = this.obj.getElementsByTagName('ul');
for (var i = 0; i < menus.length; i++) {
// 找到菜单的父节点 (包括标题链接部分)
var menu = menus[i].parentNode;
// 如果菜单的父节点就是根菜单, 显示一般的菜单
if(menu.parentNode === this.obj) {
new Menu(menu, opacity);
// 如果菜单的父节点不是根菜单, 说明当前菜单是子菜单
} else {
new Menu(menu, opacity, 1);
// 在子菜单的标题链接上加上 class 名, 以便定义样式
menu.firstChild.className += ' subtitle';
}
}
4. 菜单的初始化
添加一个参数, 以识别是否为子菜单 (三级以上的菜单); 去除 overflow:hidden; 的设置, 否则子菜单无法显示出来.
initialize: function(target, opacity, sub) {
this.util = new MenuUtil();
// 获取目标菜单 (没多余元素)
this.obj = this.util.cleanWhitespace(target);
// 定义透明度, 默认透明
this.opacity = opacity || 1;
/* multi 2009/06/11 ADD START */
// 是否为子菜单
this.sub = sub || -1;
/* multi 2009/06/11 ADD START */
// 获取菜单
this.menu = this.obj.childNodes
// 重要! 如果菜单不包含菜单项, 则不进行处理
if (this.menu.length < 2) { return; }
// 菜单标题和菜单体
this.title = this.menu[0];
this.body = this.menu[1];
// 定义初始样式
this.util.setStyle(this.body, 'visibility', 'hidden');
this.util.setStyle(this.body, 'position', 'absolute');
/* multi 2009/06/11 DELETE START */
//this.util.setStyle(this.body, 'overflow', 'hidden');
/* multi 2009/06/11 DELETE END */
this.util.setStyle(this.body, 'display', 'block');
// 添加监听器
this.addListener(this.obj, 'mouseover', this.util.bind(this, this.activate), false);
this.addListener(this.obj, 'mouseout', this.util.bind(this, this.deactivate), false);
}
5. 对菜单和子菜单分开处理
菜单 (二级菜单) 的位置是相对于窗口的, 而子菜单 (三级或以上菜单) 是的位置是相对于上一级菜单的, 所以必须判断是哪种菜单类型, 并以不同的方法来确定位置. 另外, 两者要显示的位置也不相同, 所以在激活方法内还需要以不同的方式将 top 和 left 位置计算出来.
// 获取当前菜单体的位置 (子菜单)
if(this.sub == 1) {
var pos = this.util.currentOffset(this.title);
var left = this.util.getWidth(this.body);
var top = pos[1];
// 获取当前菜单体的位置 (菜单)
} else {
var pos = this.util.cumulativeOffset(this.title);
var left = pos[0];
var top = pos[1] + this.util.getHeight(this.title);
// 子菜单不需要设置不透明度, 否则会形成多重透明, 效果不好.
this.util.setStyle(this.body, 'opacity', this.opacity);
}
6. 添加当前菜单的 className
为了更好的表示当前菜单的上级菜单, 在激活菜单的时候为当前菜单加上 className.
// 当前选中菜单加上 class 名为, 以便定义样式
this.title.className += ' current';
并且在解除菜单的时候移除菜单上的 className.
// 离开菜单时取消当前菜单上的 class 名, 恢复原本的样式
this.title.className = this.title.className.replace('current', '');
演示主题:
以 WordPress 自带主题 default 为基础, 仅做学习参考使用, 修改过的文件有 header.php 和 style.css, 添加了文件js/menu.js