多级导航菜单, 是指菜单存在多个层次, 层层嵌套, 当鼠标移动到某个菜单时, 如果其包含子菜单则将相应的子菜单显示出来. 本文将提供此功能在 WordPress 的实现方法, 一般的 HTML 页面和其他程序也可以加工套用.
时隔 9 个多月, 关于导航菜单的话题又回来了. 上次写到三级菜单就不写了是因为我发现自己根本用不上, 就没去研究. 最近我在做一个小玩意儿用到了这个, 所以把它给做了出来并集成到 iNove 主题上. 因为内容繁多, 所以还是以之前的文章和代码作为基础来展开讲解, 希望这个文章会对大家有所帮助.
为了简化处理, 明确目标, 这次我们会以二级导航菜单作为原型进行扩展. 本文只对多级菜单相关处理进行讨论, 其他内容请参考以前的几篇关于菜单导航的文章, 文章链接你可以在本文相关话题中找到.
这是效果演示
WordPress 导航菜单
二级导航菜单
淡出淡入导航菜单
滚动导航菜单
jQuery 导航菜单
多级导航菜单
点选式导航菜单 (待定话题)
因为菜单由本来的二级菜单变成了多级菜单, 所以菜单结构会有些变化 (会有新的更深层的子菜单加入). 另外, 页面的样式和脚本都会有相应的变更, 重点在于对 JavaScript 的加工.
主要操作如下:
1. 鼠标移动到一级菜单的菜单项时, 如果该菜单项包含了二级菜单, 那么在下方显示其子菜单.
2. 鼠标移动到 N (N >= 2) 级菜单的菜单项时, 如果该菜单项包含了 N+1 级菜单, 那么在右侧显示其子菜单.
1. 调出无限级菜单 (子分类)
是否还记得如何设定列表的深度? 可以通过参数 depth 来进行设定. 当我们将深度设为 1 时不显示子分类, 将深度设为 2 时显示二级菜单. 现在我们要无限制层数的菜单, 所以可以去除这个参数, 系统会自动返回所有菜单. 所以显示菜单的代码如下:
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
下载主题: default_with_menubar_5.zip