看图说话,下面是利用自定义的菜单导航栏插件simpleMenu建立的网站导航示例:html
插件默认提供的是如上图的导航栏样式,即一二级菜单为横向分布;三四级菜单为纵向分布。数组
使用插件时,能够修改默认参数,目前插件提供了设置菜单的分布方式:横向或纵向;菜单的位置:依赖上一级菜单栏的定位:上下左右定位。函数
修改调用参数,将一二级菜单改成纵向排列;并将三级菜单的显示位置改成二级菜单栏的右侧(其余的和默认保持一致),修改后运行效果以下图:学习
细心的观察,会发现上面两个菜单导航栏的风格基本一致,只是在局部有所调整。之因此这样设计,是由于随着网站的逐渐丰富,菜单导航栏的需求也会常常发生变化。经过参数化配置菜单,可以在现有的基础上,快速适应新的变化。固然这样作也会对咱们编写插件提出更要的要求和更大的挑战,何况始终没法作一个万能的菜单出来!动画
jQuery UI的功能很强大,主要分为3个部分:交互、微件(Widget)和效果库。其中交互是一些与鼠标交互的内容如拖动、缩放、选择和排序等;微件主要是一些节目的扩展包括对话框、日历、进度条,固然也有相似的菜单导航;效果库是用于提供丰富的动画效果,让动画不在局限于animate方法。网站
虽然jQuery UI已经提供了相似导航的功能,但其功能和效果每每难以知足复杂项目的需求。下面说说重点,我所但愿实现的插件功能是:插件对外提供建立菜单的接口函数,输入参数为包含菜单项信息的数据源,输出为对应的多级菜单。利用jQuery UI的Widget库,咱们能够轻松自定义新的jQuery UI微件,知足上述功能需求。this
2.1 建立Widget插件spa
经过Widget Factory建立微件的方法是向$.widget()传递一个widget的名称和原型对象。例如在菜单导航栏插件中,定义方式以下: 插件
var menu = $.widget('myPlugin.simpleMenu', { version: "1.0.1", pathPrefix: $.myPlugin.config.pathPrefix, options: { /*default params*/ }, _create: function () { }, /*具体实现函数略*/ _destroy: function() { }, });
这里在myPlugin命名空间下定义了一个名为simpleMenu的Widget,即菜单导航栏插件。在给定的原型对象中还定义了插件的版本,路径前缀,默认参数,建立函数,实现函数等。要注意的是,Widget插件的真正建立时从_create开始的,这个函数咱们能够重写覆盖,至于内部是怎么调用的这个过程插件对咱们透明。设计
2.2 须要实现的具体功能
以前已经说了,菜单导航栏插件的输入数据为包含菜单项信息的数据源。考虑到通用性,推荐使用JSON做为数据源。JSON数据的组装以及读取都很简单,难点在于如何将JSON数据转换为任意层次的菜单,而且保证菜单每一级菜单项都能正确地绑定监听事件(鼠标点击或鼠标通过、离开等事件)。
生成的菜单导航和原始的JSON数据都是具备层次结构的,为了生成与JSON层次相同的菜单层次,最简单有效的方法就是递归调用。然而多级菜单的递归调用并非一次成型的,而是按需调用——用户点击或通过某个菜单项时才触发一次递归调用。下面首先贴出createMenu的代码,这段代码主要实现的就是根据menuList(待加载的菜单项集合)和menuPath(上一级菜单项的路径)来加载当前菜单项集合。例如第一次执行该函数时,menuList为原始的菜单项-JSON数组,menuPath为"item"-顶层初始值;遍历menuList的每一个元素(对应一级菜单的菜单项),首先生成带初始样式的html结构;而后设置当前级别(如一级)菜单的状态,包括在文档中的位置,绑定事件,以及在菜单项过多的时候及时提供滚动换页功能;递归在绑定事件函数中产生,当用户触发绑定事件时,会调用createMenu函数,并传递两个参数:触发菜单项的下级菜单集合以及路径信息。
createMenu私有函数的实现以下:
_createMenu: function(menuList, menuPath){ var pathArr = menuPath.split('-'), level = pathArr.length, menuClass = ".menu" + level, menuDiv, menuUl, content = "", menuSubflag, menuName, menuItem; if((menuDiv = this.element.find(menuClass)).length === 0){ return; } menuUl = menuDiv.children("ul"); /*循环生成level级菜单的html代码并添加到菜单ul中*/ for(var i = 0, len = menuList.length; i < len; i++){ menuItem = menuList[i]; menuName = menuItem.menuName; menuSubflag = ""; /*判断菜单项是否为叶子菜单(不含子菜单)*/ if(menuItem.subMenu && menuItem.subMenu.length > 0){ if(level > 1){ menuName += "<div class='sub'></div>"; } } else{ menuSubflag = " class = 'leaf'"; } content += "<li data-path='" + menuPath + "-" + (i + 1) + "'" + menuSubflag + ">" + menuName + "</li>"; } menuUl.html(content); menuDiv.show(); /*显示当前的菜单栏(有些菜单栏默认设置为隐藏)*/ /*设置level级菜单栏的位置*/ this._setPosition(pathArr, menuDiv); /*绑定菜单鼠标点击或通过事件*/ this._bindMenuEvents(menuList, menuUl, level); /*设置菜单的水平滚动*/ this._setMenuScroll(); }
绑定菜单项事件函数以下:
/*菜单事件函数,处理菜单的点击或鼠标通过事件*/ _bindMenuEvents: function(menuList, menuUl, level){ /*记录当前的this指针,以便在绑定函数内部使用*/ var that = this; /*给菜单项绑定鼠标事件-鼠标点击或是鼠标通过事件*/ menuUl.children("li").bind(this.options.menuEvents[level - 1], (function(menuList){ return function(){ $(this).addClass('select').siblings().removeClass('select'); var subMenu = menuList[$(this).index()].subMenu; that._createMenu(subMenu, $(this).attr("data-path")); } })(menuList)); /*给叶子菜单项绑定鼠标点击事件-执行跳转(一级菜单不执行跳转)*/ if(level <= 1){ return; } menuUl.children("li.leaf").click(function(menuList){ return function(){ var menuItem = menuList[$(this).index()]; that._executeMenu(menuItem); }; }(menuList)); }
上面的代码实现了任意级菜单的动态生成。接下来须要作的只是在此基础上丰富插件,增长灵活性。后续开发中,我增长了设置菜单排列方向的功能:好比设置一级菜单横向或纵向排列;还增长了菜单定位功能:好比生成的二级菜单相对于触发它的一级菜单项的位置靠右或正下方显示。
在提供额外功能的同时,也面临更多的问题:首先这些功能增长了使用者的学习成本,其次有些功能如菜单定位中存在一些并不合理的设置。这些都期待在之后不断完善!
插件的使用方法:首先须要提供一个格式正确的JSON菜单数据,目前自定义的插件在处理时主要依据JSON中的menuName、subMenu、actionList等key来取得实际的value值,因此提供的JSON数据必须相似下面的例子形式。而后使用插件名来调用插件时:注意在插件参数中除了提供菜单的基本信息外,还能够指定一个回调函数来处理连接的跳转;经过回调函数实现了菜单生成插件自己与执行跳转逻辑的解耦,方便使用者根据实际状况编写回调函数来选择跳转的方式。
$(document).ready(function(){ var menuJson = [ {"menuName": "导航菜单栏目1", "menuCode": "", "subMenu": [ {"menuName": "二级菜单1", "menuCode": "", "subMenu": [ {"menuName": "三级菜单1", "menuCode": "", "subMenu": [ {"menuName": "四级菜单1", "menuCode": "", "actionList": "www.abchina.com", "subMenu": []} ] }, {"menuName": "三级菜单2", "menuCode": "", "subMenu": [ {"menuName": "四级菜单1", "menuCode": "", "subMenu": []} ] } ] }, {"menuName": "二级菜单2", "menuCode": "", "subMenu": []} ] }, {"menuName": "导航菜单栏目2", "menuCode": "", "subMenu": [ {"menuName": "二级菜单3", "menuCode": "", "subMenu": [ {"menuName": "三级菜单3", "menuCode": "", "subMenu": []} ] }, {"menuName": "二级菜单4", "menuCode": "", "subMenu": []} ] }, {"menuName": "导航菜单栏目3", "menuCode": "", "subMenu": [ {"menuName": "二级菜单5", "menuCode": "", "subMenu": []} ] } ]; $(".menu").simpleMenu({menuList: menuJson, executeMenu: function(actionList){ window.location.href = actionList; }}); });
本博文为我的原创做品,转载时请注明出处。若有建议,请直接回帖交流,谢谢!