1. 程式人生 > >JSON 無限摺疊選單

JSON 無限摺疊選單

最近看了一篇關於JSON無限摺疊選單的文章 感覺寫的不錯,也研究了下程式碼,自己也實際應用到公司專案裡了,很實用,推薦一下.
推薦地址:http://www.cnblogs.com/tugenhua0707/p/3474791.html

示例
可根據自己想要的樣式以及效果自定義更改。

js程式碼:

/**
 * JSON無限摺疊選單
 * @constructor {AccordionMenu}
 * @param {options} 物件
 */
 function AccordionMenu(options) {
    this.config = {
        containerCls        : '.wrap-menu',                // 外層容器
        menuArrs            :  '',                         //  JSON傳進來的資料
        type                :  'click',                    // 預設為click 也可以mouseover
        renderCallBack      :  null,                       // 渲染html結構後回撥
        clickItemCallBack   : null                         // 每點選某一項時候回撥
    };
    this.cache = {
        
    };
    this.init(options);
 }

 
 AccordionMenu.prototype = {

    constructor: AccordionMenu,

    init: function(options){
        this.config = $.extend(this.config,options || {});
        var self = this,
            _config = self.config,
            _cache = self.cache;
        
        // 渲染html結構
        $(_config.containerCls).each(function(index,item){
            
            self._renderHTML(item);

            // 處理點選事件
            self._bindEnv(item);
        });
    },
    _renderHTML: function(container){
        var self = this,
            _config = self.config,
            _cache = self.cache;
        var ulhtml = $('<ul></ul>');
        $(_config.menuArrs).each(function(index,item){
            var lihtml = $('<li><h2>'+item.name+'</h2></li>');

            if(item.submenu && item.submenu.length > 0) {
                self._createSubMenu(item.submenu,lihtml);
            }
            $(ulhtml).append(lihtml);
        });
        $(container).append(ulhtml);
        
        _config.renderCallBack && $.isFunction(_config.renderCallBack) && _config.renderCallBack();
        
        // 處理層級縮排
        self._levelIndent(ulhtml);
    },
    /**
     * 建立子選單
     * @param {array} 子選單
     * @param {lihtml} li項
     */
    _createSubMenu: function(submenu,lihtml){
        var self = this,
            _config = self.config,
            _cache = self.cache;
        var subUl = $('<ul></ul>'),
            callee = arguments.callee,
            subLi;
        
        $(submenu).each(function(index,item){
            var url = item.url || 'javascript:void(0)';

            subLi = $('<li><a href="'+url+'">'+item.name+'</a></li>');
            if(item.submenu && item.submenu.length > 0) {

                $(subLi).children('a').prepend('<img src="images/blank.gif" alt=""/>');
                callee(item.submenu, subLi);
            }
            $(subUl).append(subLi);
        });
        $(lihtml).append(subUl);
    },
    /**
     * 處理層級縮排
     */
    _levelIndent: function(ulList){
        var self = this,
            _config = self.config,
            _cache = self.cache,
            callee = arguments.callee;
       
        var initTextIndent = 2,
            lev = 1,
            $oUl = $(ulList);
        
        while($oUl.find('ul').length > 0){
            initTextIndent = parseInt(initTextIndent,10) + 2 + 'em'; 
            $oUl.children().children('ul').addClass('lev-' + lev)
                        .children('li').css('text-indent', initTextIndent);
            $oUl = $oUl.children().children('ul');
            lev++;
        }
        $(ulList).find('ul').hide();
        $(ulList).find('ul:first').show();    
    },
    /**
     * 繫結事件
     */
    _bindEnv: function(container) {
        var self = this,
            _config = self.config;

        $('h2,a',container).unbind(_config.type);
        $('h2,a',container).bind(_config.type,function(e){
            if($(this).siblings('ul').length > 0) {
                $(this).siblings('ul').slideToggle('slow').end().children('img').toggleClass('unfold');
            }

            $(this).parent('li').siblings().find('ul').hide()
                   .end().find('img.unfold').removeClass('unfold');
            _config.clickItemCallBack && $.isFunction(_config.clickItemCallBack) && _config.clickItemCallBack($(this));

        });
    }
 };

選單預設樣式:

.wrap-menu {
	overflow:auto;
	width:300px;
	background:#F6F6F6;
	font:12px/1.5 Tahoma,Arial,sans-serif
}
.wrap-menu ul {
	list-style:none;
	margin:0;
	padding:0;
}
.wrap-menu ul li {
	text-indent:3em;
	white-space:nowrap;
}
.wrap-menu ul li h2 {
	cursor:pointer;
	height:100%;
	width:100%;
	margin:0 0 1px 0;
	font:12px/31px '宋體';
	color:#fff;
	background:red;
}
.wrap-menu ul li a {
	display:block;
	outline:none;
	height:25px;
	line-height:25px;
	margin:1px 0;
	color:#1A385C;
	text-decoration:none;
}
.wrap-menu ul li img {
	margin-right:10px;
	margin-left:-17px;
	margin-top:9px;
	width:7px;
	height:7px;
	background:url(images/arrow.gif) no-repeat;
	border:none;
}
.wrap-menu ul li img.unfold {
	background-position:0 -9px;
}
.wrap-menu ul li a:hover {
	background-color:#ccc;
	background-image:none;
}

引用程式碼:(初始化呼叫)

$(function(){
    new AccordionMenu({menuArrs:testMenu});
});

提示:
new AccordionMenu({menuArrs:testMenu}); 其中testMenu 就是下面定義的JSON格式

測試選單資料:

var testMenu=[
    {
        "name": "一級選單",
        "submenu": [
            {
                "name": "二級選單",
                "url": ""
            },
            {
                "name": "二級選單",
                "url": ""
            }
        ]
    },
    {
        "name": "一級選單",
        "submenu": [
            {
                "name": "二級選單",
                "url": ""
            },
            {
                "name": "二級選單",
                "submenu": [
                    {
                        "name": "三級選單",
                        "submenu": [
                            {
                                "name": "四級選單",
                                "url": ""
                            }
                        ]
                    },
                    {
                        "name": "三級選單",
                        "url": ""
                    }
                ]
            },
            {
                "name": "二級選單",
                "url": ""
            },
            {
                "name": "二級選單",
                "submenu": [
                    {
                        "name": "三級選單",
                        "submenu": [
                            {
                                "name": "四級選單",
                                "url": ""
                            },
                            {
                                "name": "四級選單",
                                "submenu": [
                                    {
                                        "name": "五級選單",
                                        "url": ""
                                    },
                                    {
                                        "name": "五級選單",
                                        "url": ""
                                    }
                                ]
                            }
                        ]
                    },
                    {
                        "name": "三級選單",
                        "url": ""
                    }
                ]
            },
            {
                "name": "二級選單",
                "url": ""
            }
        ]
    },
    {
        "name": "一級選單",
        "submenu": [
            {
                "name": "二級選單",
                "url": ""
            },
            {
                "name": "二級選單",
                "url": ""
            },
            {
                "name": "二級選單",
                "url": ""
            }
        ]
    }
];

為了方便,自行寫了Java無限選單遞迴封裝的程式碼以及無限選單程式碼,如下:

Java無限選單遞迴封裝的程式碼:(controller)

/**
 * 初始化載入選單,獲取選單列表
 *
 * @return
 * @throws Exception
 */
@ResponseBody
@RequestMapping(value = "/getMenuList", method = RequestMethod.POST, produces = {
        "application/json; charset=UTF-8"})
public Object getMenuList(@RequestBody JSONObject jsonP) throws Exception {
    JSONArray menuArray = new JSONArray();
    JSONObject menuList = new JSONObject();
    JSONObject menuObject = new JSONObject();
    List<TbMenu> menuListAll = iMenuService.getMenuList();
    JSONArray menuArrayAll = JSONArray.parseArray(JSONArray.toJSONString(menuListAll));
    if (menuArrayAll != null && !menuArrayAll .isEmpty()) {
        for (int i = 0; i < menuArrayAll.size(); i++) {
            JSONObject jsonObj = menuArrayAll.getJSONObject(i);
            JSONArray menuItems = menuObject.getJSONArray(jsonObj.getString("menuTid"));
            if (menuItems == null || menuItems.isEmpty()) {
                JSONArray jsonArr = new JSONArray();
                jsonArr.add(jsonObj);
                menuObject.put(jsonObj.getString("menuTid"), jsonArr);
            } else {
                menuItems.add(jsonObj);
                menuObject.put(jsonObj.getString("menuTid"), menuItems);
            }
            JSONObject menu = new JSONObject();
            menu.put("idx", i + 1); // 序號
            menu.put("id", jsonObj.getString("menuPid")); // ID
            menu.put("name", jsonObj.getString("menuName"));
            menuArray.add(menu);
        }
    }
    menuList.put("menuList", getMenuListByTid(menuObject, jsonP.getIntValue("menuTid")));
    menuList.put("mArray", menuArray);
    return menuList;
}

/**
 * 遞進選單列表
 *
 * @param menuTid
 * @return
 */
private JSONArray getMenuListByTid(JSONObject menuObject, Integer menuTid) {
    JSONArray menuArray = new JSONArray();
    JSONArray menuList = menuObject.getJSONArray(menuTid.toString());
    if (menuList != null && !menuList.isEmpty()) {
        for (int i = 0; i < menuList.size(); i++) {
            JSONObject menuItem = new JSONObject();
            TbMenu tbMenu = menuList.getObject(i, TbMenu.class);
            menuItem.put("name", tbMenu.getMenuName()); // 選單名稱
            JSONArray menuListItem = menuObject.getJSONArray(tbMenu.getMenuPid().toString());
            if (menuListItem == null || menuListItem.isEmpty()) {
                menuItem.put("target", tbMenu.getMenuTarget()); // 跳轉方式
                menuItem.put("url", tbMenu.getMenuUrl()); // 訪問url
            } else {
                menuItem.put("submenu", getMenuListByTid(menuObject, tbMenu.getMenuPid()));
            }
            menuArray.add(menuItem);
        }
    }
    return menuArray;
}

封裝的初始化js程式碼:

/*
 * metismenu - v1.1.3
 * Easy menu jQuery plugin for Twitter Bootstrap 3
 * https://github.com/onokumus/metisMenu
 *
 * Made by Osman Nuri Okumus
 * Under MIT License
 */
;(function ($, window, document, undefined) {
    // 資料初始化
    var menuDatas = null;
    $.ajax({
           url: "/menu/getMenuList",
           type: "POST",
           data: JSON.stringify({"menuTid": 0}), //傳參為0,載入根(一級)選單,1載入二級選單,2載入三級選單...
           dataType: "json",
           async: false, //開啟同步,先載入完ajax請求,再繼續往下執行選單渲染程式碼
           contentType: "application/json",
           success: function (menuData) {
               // console.log("成功:" + JSON.stringify(menuData));
               menuDatas = JSON.stringify(menuData.menuList);
           },
           error: function (msg) {
               console.log("失敗:" + msg);
           }
    });
    var renderCallBack = null;
    // 渲染html結構
    $(".sidebar-collapse").each(function (index, item) {
        _renderHTML(item);
    });

    /**
     * 建立主選單
     * @param {array} 主選單
     * @param {lihtml} li項
     */
    function _renderHTML(container) {
        var ulhtml = $('<ul class="nav" id="side-menu"></ul>'),
            lihtml;
        var liheader = $('<li class="nav-header"><div class="dropdown profile-element">'
            + '<img alt="image" src="/img/iconfont-logo.png" /></div><div class="logo-element">D+</div></li>');
        $(ulhtml).append(liheader);
        $(menuDatas).each(function (index, item) {
            var url = (item.url) || ('/?path=welcome&random=' + Math.random());

            if (item.submenu && item.submenu.length > 0) {
                lihtml = $('<li><a href="' + url + '"><i class="glyphicon glyphicon-folder-open"></i>'
                    + '<span class="nav-label">' + item.name + '</span>'
                    + ((item.submenu && item.submenu.length > 0) ? '<span class="fa arrow"></span>' : '')
                    + '</a></li>');
                _createSubMenu(item.submenu, lihtml);
            } else if (item.target && item.target.length > 0) {
                lihtml = $('<li><a href="' + url + '" target="' + item.target
                    + '"><i class="glyphicon glyphicon-folder-open"></i>'
                    + '<span class="nav-label">' + item.name + '</span></a></li>');
            } else {
                lihtml = $('<li><a class="J_menuItem" href="' + url
                    + '"><i class="glyphicon glyphicon-folder-open"></i>'
                    + '<span class="nav-label">' + item.name + '</span></a></li>');
            }
            $(ulhtml).append(lihtml);
        });
        $(container).append(ulhtml);

        renderCallBack && $.isFunction(renderCallBack) && renderCallBack();
    }

    /**
     * 建立子選單
     * @param {array} 子選單
     * @param {lihtml} li項
     */
    function _createSubMenu(children, lihtml) {
        var subUl = $('<ul class="nav nav-second-level"></ul>'),
            callee = arguments.callee,
            subLi;

        $(children).each(function (index, item) {
            var url = (item.url) || ('/?path=welcome&random=' + Math.random());

            if (item.submenu && item.submenu.length > 0) {
                subLi = $('<li><a href="#"><span class="nav-label">' + item.name + '</span>'
                    + ((item.submenu && item.submenu.length > 0) ? '<span class="fa arrow"></span>' : '')
                    + '</a></li>');
                callee(item.submenu, subLi);
            } else if (item.target && item.target.length > 0) {
                subLi = $('<li><a href="' + url + '" target="' + item.target + '">' + item.name + '</a></li>');
            } else {
                subLi = $('<li><a class="J_menuItem" href="' + url + '">' + item.name + '</a></li>');
            }
            $(subUl).append(subLi);
        });
        $(lihtml).append(subUl);

        // 處理層級縮排
        _levelIndent(subUl);
    }

    /**
     * 處理層級縮排
     */
    function _levelIndent(ulList) {
        var lev = 1,
            $oUl = $(ulList);

        while ($oUl.find('ul').length > 0) {
            $oUl.children().children('ul').addClass("coll");
            $oUl = $oUl.children().children('ul');
            lev++;
        }
        $(ulList).find('ul').slideUp(200);
        // $(ulList).find('ul:first').slideUp(200);
    }

    var pluginName = "metisMenu",
        defaults = {
            toggle: true,
            doubleTapToGo: false
        };

    function Plugin(element, options) {
        this.element = $(element);
        this.settings = $.extend({}, defaults, options);
        this._defaults = defaults;
        this._name = pluginName;
        this.init();
    }

    Plugin.prototype = {
        init: function () {
            if (menuDatas.length == 0) {
                return;
            }
            var $this = this.element,
                $toggle = this.settings.toggle,
                obj = this;

            if (this.isIE() <= 9) {
                $this.find("li.active").has("ul").children("ul").collapse("show");
                $this.find("li").not(".active").has("ul").children("ul").collapse("hide");
            } else {
                $this.find("li.active").has("ul").children("ul").addClass("collapse in");
                $this.find("li").not(".active").has("ul").children("ul").addClass("collapse");
            }

            //add the "doubleTapToGo" class to active items if needed
            if (obj.settings.doubleTapToGo) {
                $this.find("li.active").has("ul").children("a").addClass("doubleTapToGo");
            }

            $this.find("li").has("ul").children("a").on("click" + "." + pluginName, function (e) {
                e.preventDefault();

                //Do we need to enable the double tap
                if (obj.settings.doubleTapToGo) {

                    //if we hit a second time on the link and the href is valid, navigate to that url
                    if (obj.doubleTapToGo($(this)) && $(this).attr("href") !== "#" && $(this).attr("href") !== "") {
                        e.stopPropagation();
                        document.location = $(this).attr("href");
                        return;
                    }
                }

                $(this).parent("li").toggleClass("active").children("ul").collapse("toggle");

                if ($toggle) {
                    $(this).parent("li").siblings().removeClass("active").children("ul.in").collapse("hide");
                }

            });
        },

        isIE: function () { //https://gist.github.com/padolsey/527683
            var undef,
                v = 3,
                div = document.createElement("div"),
                all = div.getElementsByTagName("i");

            while (
                div.innerHTML = "<!--[if gt IE " + (++v) + "]><i></i><![endif]-->",
                    all[0]
                ) {
                return v > 4 ? v : undef;
            }
        },

        //Enable the link on the second click.
        doubleTapToGo: function (elem) {
            var $this = this.element;

            //if the class "doubleTapToGo" exists, remove it and return
            if (elem.hasClass("doubleTapToGo")) {
                elem.removeClass("doubleTapToGo");
                return true;
            }

            //does not exists, add a new class and return false
            if (elem.parent().children("ul").length) {
                //first remove all other class
                $this.find(".doubleTapToGo").removeClass("doubleTapToGo");
                //add the class on the current element
                elem.addClass("doubleTapToGo");
                return false;
            }
        },

        remove: function () {
            this.element.off("." + pluginName);
            this.element.removeData(pluginName);
        }

    };

    $.fn[pluginName] = function (options) {
        this.each(function () {
            var el = $(this);
            if (el.data(pluginName)) {
                el.data(pluginName).remove();
            }
            el.data(pluginName, new Plugin(this, options));
        });
        return this;
    };

})(jQuery, window, document);

轉載請註明出處!