SOURCE

console 命令行工具 X clear

                    
>
console
class Menu {
    constructor(place, template) {
        this.place = place;
        this.template = template;

        function elt(tagName, textContent = "", classnames = "", styles = {}) {
            let el = document.createElement(tagName);
            el.textContent = textContent;
            if (typeof classnames == 'string' && classnames.trim()) {
                el.classList.add(classnames);
            } else if (classnames instanceof Array && classnames.length > 0) {
                el.classList.add(...classnames);
            }
            Object.assign(el.style, styles);
            return el;
        }
        let that = this;
        let menuContainer = elt("div", "", ["menu-container", "menu-float-clear"]);
        this.place.appendChild(menuContainer);

        let level1s = [];
        function templateToDOM() {
            let level = 0;
            function deep(obj, parent) {
                for (let key in obj) {
                    let item = obj[key];
                    let el = null;
                    item.label = item.label ? item.label : "无标题菜单";
                    if (level == 0) {
                        el = elt('div', "", ["menu-item-level-1", "hover-hightlight"]);
                        let innerwrap = elt('span', item.label, "menu-item-level1-innerwrap");
                        el.appendChild(innerwrap);
                        parent.appendChild(el);
                        level1s.push(el);
                    } else {
                        el = elt('div', "", ["sub-menu-item", "hover-hightlight", "show-submenu"]);
                        let innerwrap = elt('span', item.label, "menu-item-innerwrap");
                        el.appendChild(innerwrap);
                        parent.appendChild(el);
                    }

                    if (el && item.onclick) {
                        el.addEventListener("click", function (e) {
                            e.stopPropagation();
                            item.onclick();
                        });
                    }

                    if (item.sub && item.sub instanceof Array) {
                        let newparent = level == 0 ? elt("div", "", ["sub-menu", "sub-menu-below"]) : elt("div", "", ["sub-menu", "sub-menu-left"]);
                        if (el) {
                            el.appendChild(newparent);
                            if (!el.classList.contains('menu-item-level-1')) {
                                el.classList.add('submenu-parent');
                            }
                        }
                        ++level;
                        deep(item.sub, newparent);
                        --level;
                    }
                }
            }
            deep(that.template, menuContainer);

            let width = 0;
            Array.from(menuContainer.children).forEach(function (item) {
                width += parseInt(getComputedStyle(item).width);
            });
            menuContainer.style.minWidth = width + "px";

            function doc_mousedown(e) {
                e.stopPropagation();
                level1s.forEach(function (item) {
                    item.classList.remove('show-submenu-level1');
                });
            }
            document.addEventListener("mousedown", doc_mousedown);

            function level1s_mousdown(e) {
                e.stopPropagation();
                let that = this;
                level1s.forEach(function (item) {
                    if (item != that) {
                        item.classList.remove('show-submenu-level1');
                    }
                });
                this.classList.add('show-submenu-level1');
            }
            function level1s_mouseenter(e) {
                let contain = level1s.some(function (item) {
                    return item.classList.contains('show-submenu-level1');
                });
                if (contain) {
                    let that = this;
                    level1s.forEach(function (item) {
                        if (item != that) {
                            item.classList.remove("show-submenu-level1");
                        }
                    });
                    this.classList.add('show-submenu-level1');
                }
            }
            level1s.forEach(function (item) {
                item.addEventListener("mousedown", level1s_mousdown);
                item.addEventListener('mouseenter', level1s_mouseenter);
            });
        }
        templateToDOM();
    }
}
let place = document.querySelector("#root");
let template = [
    {
        label: "文件",
        sub: [
            {
                label: "打开",
                onclick: function () {
                    console.log(1)
                }
            },
            {
                label: "新建",
            },
            {
                label: "关闭",
                sub: [
                    {
                        label: "关闭项目"
                    },
                    {
                        label: "关闭文件",
                        sub: [
                            {
                                label: "关闭并保存"
                            },
                            {
                                label: "直接关闭",
                                sub: [
                                    {
                                        label: "item1"
                                    },
                                    {
                                        label: 'item2',
                                    }
                                ]
                            }
                        ]
                    }
                ]
            }
        ]
    },
    {
        label: "编辑"
    },
    {
        label: "运行"
    },
    {
        label: "帮助",
        sub: [
            {
                label: "打开开发者工具"
            }
        ]
    }
]
let menu = new Menu(place, template);
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=, initial-scale=">
	<meta http-equiv="X-UA-Compatible" content="">
	<title></title>
</head>
<body>
	<div id="root"></div>
</body>
</html>
* {
    margin: 0;
    padding: 0;
}
#root {
    position: relative;
}
.menu-container {
    position: absolute;
    left: 0;
    top: 0;
    background: #23272e;
    color: #eee;
    cursor: pointer;
    user-select: none;
    font-size: 14px;
    height: 30px;
    line-height: 30px;
}
.menu-item-level-1 {
    position: relative;
    float: left;
    height: 100%;
}

.menu-item-level-1>.menu-item-level1-innerwrap {
    display: block;
    padding: 0 20px;
    width: 100%;
    white-space: nowrap;
}
.hover-hightlight:hover {
    background: #35393d;
}
.sub-menu-item {
    position: relative;
    white-space: nowrap;
    text-align: left;
    font-size: 12px;
}
.sub-menu-item>.menu-item-innerwrap {
    padding-left: 2em;
    padding-right: 4em;
}
.sub-menu {
    background: #23272e;
    display: none;
    min-width: 120px;
}
.sub-menu-left {
    position: absolute;
    left: calc(100% + -1px);
    top: 0;
}
.sub-menu-below {
    position: absolute;
    left: 0;
    top: calc(100% + 0px);
}
.show-submenu:hover>.sub-menu {
    display: block;
}
.show-submenu-level1>.sub-menu {
    display: block;
}
.menu-float-clear::after {
    content: "";
    display: block;
    height: 0;
    clear: both;
    visibility: hidden;
}
.submenu-parent::after {
    position: absolute;
    right: 10%;
    top: 50%;
    transform: translateY(-50%);
    content: ">";
    font-family: "幼圆";
    font-weight: bold;
}