看圖說話,下面是利用自定義的菜單導航欄插件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; }}); });
本博文爲我的原創做品,轉載時請註明出處。若有建議,請直接回帖交流,謝謝!