JS左侧菜单工具栏
wenruns 人气:0摘要
该js脚本可帮助你快速实现左侧菜单工具栏。通过js封装成一个方法类,直接new该对象即可快速生成左侧菜单工具栏。
一、效果展示
二、menu.js文件
(1)WenMenuNode节点
let WenMenuNode = function ({ text, wenMenu, attributes = {}, subs = [], parentElement = null, iconHTML = '', level = 1, parentNode = null, isActive = false, onLaunch = null, }) { this._level = level; this._text = text; this._attributes = attributes; this._wenMenu = wenMenu; this._subs = subs; this._onLaunch = onLaunch; this._childHeight = 0; this._height = 0; this.style = { childHeight: 0, } this._parentElement = parentElement; this._parentNode = parentNode; this._element = this._wenMenu.createElement('li', { class: "wen-menu-li", }); this._textElement = this._wenMenu.createElement('a', this._attributes); this._iconHTML = iconHTML; this._childNodes = []; this._childElement = null; this._activeChild = null; if (this._parentElement) this._parentElement.append(this._element); this._isActive = isActive; if (this._isActive) { if (this._level == 1) { this._wenMenu._activeMenu = this; } else if (this._parentNode) { this._parentNode._activeChild = this; } } this.create().onLaunch(); } WenMenuNode.prototype.create = function () { let a = this._textElement; let icon = this._wenMenu.createElement('i', { class: "wen-menu-icon", }) if (this._level > 1) { a.innerHTML = '<span class="wen-menu-tree">--</span>'; } icon.innerHTML = this._iconHTML; a.append(icon); a.innerHTML += `<span class="wen-menu-text">${this._text}</span>`; if (this._level == 1) { a.classList.add('wen-menu-first'); } this._element.append(a); if (this._subs.length) { let ul = this._wenMenu.createElement('ul', { class: "wen-menu-ul" + (this._level == 1 ? " wen-menu-ul-second" : ""), }); this._element.append(ul); this._childElement = ul; this._subs.forEach((item, i) => { let node = new WenMenuNode({ text: item.text, wenMenu: this._wenMenu, attributes: item.attributes, subs: item.subs, parentElement: ul, iconHTML: item.iconHTML, level: this._level + 1, parentNode: this, isActive: this._isActive && i == 0, onLaunch: (childNode) => { this._childNodes.push(childNode); if (i == this._subs.length - 1) { this.setEventListener(true); } } }); }); } else { this.setEventListener(false); } return this; } WenMenuNode.prototype.onLaunch = function () { if (this._onLaunch) { this._onLaunch.call(this._parentNode, this); } return this; } WenMenuNode.prototype.setEventListener = function (hasSub = false) { if (hasSub) { this._height = this._subs.length * this._wenMenu._menuHeight; this._childHeight = this._childElement.clientHeight; if (this._isActive) { this._textElement.setAttribute('wen-active', ''); this._textElement.setAttribute('wen-expand', ''); this.style.childHeight = this._childHeight + this._wenMenu._menuSpacing; } else { this._textElement.setAttribute('wen-icon', '') this._textElement.setAttribute('wen-collapse', ''); this.style.childHeight = 0; } this._childElement.style.height = this.style.childHeight + "px"; this._textElement.addEventListener('click', (e) => { if (this._wenMenu._autoCollapse) { this.resetHeight(); this.setHeight({ menuNode: this, }) } else { let height = 0, target = e.target; if (target.classList.value.indexOf('wen-menu-text') >= 0) { target = target.parentElement; } if (target.getAttribute('wen-expand') === null) { // todo:: 展开 height = this.style.childHeight = this._height + this._wenMenu._menuSpacing; target.setAttribute('wen-expand', ''); target.removeAttribute('wen-collapse'); } else { // todo:: 收起 height = -this.style.childHeight; this.style.childHeight = 0; target.setAttribute('wen-collapse', ''); target.removeAttribute('wen-expand'); this.resetHeight(this._childNodes) } this._childElement.style.height = this.style.childHeight + 'px'; if (this._parentNode) { this.setHeight({ menuNode: this._parentNode, direction: 'up', childHeight: height, childNode: this, }) } } }); } else { if (this._isActive) { this._textElement.classList.add('wen-active'); } this._textElement.addEventListener('click', (e) => { if (this._wenMenu._autoCollapse) { this.resetHeight(); this.setHeight({ menuNode: this._parentNode, direction: 'up', childNode: this, childHeight: this._height, }) } this.removeActive(this._wenMenu._activeMenu) this._isActive = true; this._textElement.classList.add('wen-active'); let target = e.target; if (target.classList.value.indexOf('wen-menu-text') >= 0) { target = target.parentElement; } if (target.classList.value.indexOf('wen-menu-first') >= 0) { this._wenMenu._activeMenu = this; } else if (this._parentNode) { this.addActive(this._parentNode, this) } else { this._wenMenu._activeMenu = this; } if (this._wenMenu._event) { this._wenMenu._event.call(this, e) } }); } return this; } WenMenuNode.prototype.setHeight = function ({ menuNode = null, direction = 'down', childHeight = 0, childNode = null, }) { if (!menuNode) { return 0; } menuNode._textElement.setAttribute('wen-expand', ''); menuNode._textElement.removeAttribute('wen-collapse'); if (this._wenMenu._autoCollapse) { menuNode.style.childHeight = menuNode._height; } if (direction == 'down') { if (menuNode._subs.length) { menuNode.style.childHeight += (this._wenMenu._menuSpacing * (childNode ? childNode._level : 1)); if (menuNode._isActive) { menuNode.style.childHeight += this.setHeight({ menuNode: menuNode._activeChild, }); } if (menuNode._childElement) { menuNode._childElement.style.height = menuNode.style.childHeight + "px"; } if (menuNode._parentNode) { this.setHeight({ menuNode: menuNode._parentNode, direction: 'up', childNode: menuNode, childHeight: menuNode.style.childHeight, }); } } } else { menuNode.style.childHeight += (childHeight + this._wenMenu._menuSpacing); menuNode._childElement.style.height = menuNode.style.childHeight + "px"; if (menuNode._parentNode) { this.setHeight({ menuNode: menuNode._parentNode, direction: 'up', childHeight: menuNode.style.childHeight, childNode: menuNode, }); } } return menuNode.style.childHeight; } WenMenuNode.prototype.resetHeight = function (menuNodes) { if (!menuNodes) { menuNodes = this._wenMenu._menuNodes; } menuNodes.forEach((node) => { if (node._childElement) { node.style.childHeight = 0; node._childElement.style.height = '0px'; } if (node._childNodes.length) { node._textElement.setAttribute('wen-collapse', ''); node._textElement.removeAttribute('wen-expand'); this.resetHeight(node._childNodes); } }); return this; } WenMenuNode.prototype.addActive = function (menuNode, activeChildNode) { menuNode._isActive = true menuNode._textElement.setAttribute('wen-active', ''); menuNode._textElement.removeAttribute('wen-icon'); if (this._wenMenu._autoCollapse) { menuNode._textElement.setAttribute('wen-expand', ''); menuNode._textElement.removeAttribute('wen-collapse'); } menuNode._activeChild = activeChildNode; if (menuNode._parentNode) { this.addActive(menuNode._parentNode, menuNode); } else { this._wenMenu._activeMenu = menuNode; } return this; } /** * 去除active属性 * @param WenMenuNode menuNode * @return WenMenuNode */ WenMenuNode.prototype.removeActive = function (menuNode) { menuNode._isActive = false; if (menuNode._subs.length) { menuNode._textElement.removeAttribute('wen-active'); menuNode._textElement.setAttribute('wen-icon', ''); if (this._wenMenu._autoCollapse) { menuNode._textElement.setAttribute('wen-collapse', ''); menuNode._textElement.removeAttribute('wen-expand'); } if (menuNode._activeChild) { this.removeActive(menuNode._activeChild); } } else { menuNode._textElement.classList.remove('wen-active'); } return this; }
(2) WenMenu对象
let WenMenu = function ({ ele, menus, event = null, attributes = {}, menuHeight = 35, menuSpacing = 0, autoCollapse = true, duration = 300, }) { this._ele = ele; this._duration = duration; this._menus = menus; this._event = event; this._menuNodes = []; this._autoCollapse = autoCollapse; this.style = { width: '100%', height: '100%', } this._menuElement = this.createElement('ul', attributes); this._menuElement.classList.value += 'wen-menu-ul wen-menu-ul-first'; this._ele.append(this._menuElement); this._activeMenu = null; this._menuHeight = menuHeight; this._menuSpacing = menuSpacing; this.init().createStyle().createMenu(); }; WenMenu.prototype.init = function () { if (this._ele.clientHeight) { this._ele.style.overflow = 'hidden'; this._menuElement.style['overflow-y'] = 'scroll'; let scrollWidth = this._menuElement.offsetWidth - this._menuElement.clientWidth; this.style.width = 'calc(100% + ' + scrollWidth + 'px)'; this.style.height = this._ele.clientHeight + 'px'; } return this; } /** * 创建菜单 */ WenMenu.prototype.createMenu = function () { this._menus.forEach((item, i) => { let node = new WenMenuNode({ text: item.text, attributes: item.attributes, subs: item.subs, parentElement: this._menuElement, wenMenu: this, isActive: i == 0, }); this._menuNodes.push(node); }); return this; }; /** * 创建元素 * @param tagName * @param attributes * @returns {HTMLElement} */ WenMenu.prototype.createElement = function (tagName, attributes = {}) { let ele = document.createElement(tagName); function checkValue(value) { if (Object.prototype.toString.call(value) === "[object Array]") { value = value.join(','); } else if (Object.prototype.toString.call(value) === '[object Object]') { var valueStr = ''; Object.keys(value).forEach(function (name) { valueStr += name + ":" + checkValue(value[name]) + ";"; }); value = valueStr; } return value; } if (attributes) { Object.keys(attributes).forEach((name) => { let value = checkValue(attributes[name]); ele.setAttribute(name, value); }) } return ele; }; WenMenu.prototype.createStyle = function () { let style = this.createElement('style'), head = document.querySelector('head'); style.innerHTML = ` .wen-menu-ul-first, .wen-menu-ul-first *{ padding: 0px; margin: 0px; border-spacing: 0px; list-style: none; } .wen-menu-ul-first{ width: ${this.style.width}; height: ${this.style.height}; } .wen-menu-ul { overflow: hidden; } .wen-menu-ul-first, .wen-menu-ul-second { background: rgba(0, 0, 0, 0.1); } .wen-menu-li { padding-left: 22px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .wen-menu-ul-first > .wen-menu-li { padding: 0px; } .wen-menu-ul-second > .wen-menu-li { padding: 0px 0px 0px 18px; } .wen-menu-tree { border-left: 1px dashed rgba(0, 0, 0, 1); width: 16px; } .wen-menu-text{ width: calc(100% - 16px); display: flex; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; -o-text-overflow: ellipsis; } .wen-menu-li a { display: inline-block; width: 100%; height: ${this._menuHeight}px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; position: relative; cursor: pointer; display: flex; align-items: center; } .wen-menu-ul, .wen-menu-li a[wen-icon]:after, .wen-menu-li a[wen-active]:after { -webkit-transition: all ${this._duration}ms linear; -moz-transition: all ${this._duration}ms linear; -ms-transition: all ${this._duration}ms linear; -o-transition: all ${this._duration}ms linear; transition: all ${this._duration}ms linear; } .wen-menu-li a[wen-expand]:after { -webkit-transform: scale(1.3) rotate(90deg); -moz-transform: scale(1.3) rotate(90deg); -ms-transform: scale(1.3) rotate(90deg); -o-transform: scale(1.3) rotate(90deg); transform: scale(1.3) rotate(90deg); } .wen-menu-li a[wen-collapse]:after { -webkit-transform: scale(1.3) rotate(180deg); -moz-transform: scale(1.3) rotate(180deg); -ms-transform: scale(1.3) rotate(180deg); -o-transform: scale(1.3) rotate(180deg); transform: scale(1.3) rotate(180deg); } .wen-menu-li a[wen-icon]:after { content: '▷'; position: absolute; right: 5px; font-weight: bold; } .wen-menu-li a[wen-active]:after { content: '▷'; position: absolute; right: 5px; font-weight: bold; } .wen-menu-first { padding-left: 15px !important; } .wen-menu-li a[wen-active], .wen-active { color: white; } .wen-menu-li a[wen-active].wen-menu-first, .wen-active.wen-menu-first { border-left: 3px solid white; } `; if (!head) { head = document.body; } head.append(style); return this; }
三、Example-Code
(1)html文件
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>说明文档</title> <script src="/js/canvas/menu.js"></script> <style> * { padding: 0px; margin: 0px; border-spacing: 0px; list-style: none; } body { min-height: 100vh; padding: 0px; margin: 0px; } .readme-title { background: rgba(0, 0, 50, 0.3); width: 100%; display: flex; justify-content: center; align-items: center; padding: 20px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .readme-body { width: 100%; height: calc(100% - 75px); display: flex; justify-content: start; } .readme-menu-box { width: 250px; height: 100%; position: fixed; left: 0px; top: 0px; background: rgba(100, 10, 10, 0.2); overflow: auto; } </style> </head> <body> <div class="readme-body"> <div class="readme-menu-box"> <div class="readme-title"> <h2>目录</h2> </div> </div> </div> </body> </html>
(2)JS代码
a、菜单列表
let menuOptions = [{ text: "导入数据列表", subs: [ { text: "全部数据", attributes: { "data-url": "", }, subs: [ { text: "消费金额", attributes: { "data-url": "", } }, { text: "放款金额", attributes: { "data-url": "", } }, { text: "返佣金额", attributes: { "data-url": "", } }, { text: "导入数据", attributes: { "data-url": "", } }, { text: "查看", attributes: { "data-url": "", } }, { text: "编辑", attributes: { "data-url": "", } } ] }, { text: "消费金额", attributes: { "data-url": "", } }, { text: "放款金额", attributes: { "data-url": "", } }, { text: "返佣金额", attributes: { "data-url": "", } }, { text: "导入数据", attributes: { "data-url": "", } }, { text: "查看", attributes: { "data-url": "", } }, { text: "编辑", attributes: { "data-url": "", } } ] }, { text: "异常数据列表", subs: [] }, { text: "数据修正", subs: [] }, { text: "修正审核-客服经理", subs: [] }, { text: "修正审核-财务", subs: [] }, { text: "导入日志", subs: [] }]
b、菜单实例化
window.onload = function () { new WenMenu({ ele: document.querySelector('.readme-menu-box'), // 菜单插入的位置 menus: menuOptions, event: function (e) { }, // 菜单最底端点击事件触发 attributes: {}, // 最外层ul属性设置 menuHeight: 35, // 每个菜单项的高度 autoCollapse: true, // 是否自动收起无活动菜单 }) };
加载全部内容