本文实例为大家分享了JS实现左侧菜单工具栏的具体代码,供大家参考,具体内容如下
摘要该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, // 是否自动收起无活动菜单
})
};