沃梦达 / IT编程 / 前端开发 / 正文

JS实现左侧菜单工具栏

JS实现左侧菜单工具栏 本文实例为大家分享了JS实现左侧菜单工具栏的具体代码,供大家参考,具体内容如下 摘要 该js脚本可帮助你快速实现左侧菜单工具栏.通过js封装成一个方法类,直接new该对象即可快速生成左侧菜单工具栏. 一.效果展示 二.menu

本文实例为大家分享了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, // 是否自动收起无活动菜单
            })
        };

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

本文标题为:JS实现左侧菜单工具栏