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;
}