1. 前言
现在github.io压根就打不开了,想了想,还是把以前的博客都移植过来算了。
2. 自定义菜单
以前有在后台管理项目中使用顶部标签栏切换不同菜单。标签栏过多可以通过鼠标右键
展开菜单栏进行全部关闭
或者关闭其他
等操作,最近重写这个需求时发现element ui
的el-tab
组件做标签栏很方便,于是就替换了原来自定义的标签栏,但是也遇到了不少问题,这里做一下总结,记录一下。
2-1. 效果图
3. 实现思路
这篇文章主要是讲实现右键菜单
,如何设置顶部的标签栏
与侧边的导航栏
的绑定我近期会更新另外一篇文章。这里就不介绍了。
3.1. 菜单栏结构
菜单栏
使用相对定位,每次鼠标
右键点击时获取鼠标相对于页面的位置
和鼠标在盒子中的位置
。设置菜单栏
出现到指定的位置。
.contextmenu {
margin: 0;
background: #fff;
z-index: 3000;
position: absolute;
list-style-type: none;
padding: 5px 0;
border-radius: 4px;
font-size: 15px;
font-weight: 400;
color: #333;
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
li {
margin: 0;
padding: 10px 20px;
cursor: pointer;
&:hover {
background: #eee;
color: #199a9f;
}
}
}
data() {
return {
showMenu: false,
menuTop: 0,
menuLeft: 0,
};
}
3.2. 显示隐藏菜单实现思路
3-2-1. 菜单栏显示的实现逻辑
利用contextmenu
事件阻止浏览器原生的菜单栏
出现,后自定义我们要显示的菜单栏
。正常来说我们一般在各个el-tab-pane
上绑定@contextmenu
就可以实现对应的功能,但是el-tabs
组件并没有$emit
对应的方法,加上了.native
修饰符也不起作用。所以最终决定在每次路由
改变时获取到所有的el-tab-pane
节点,手动的去绑定contextmenu
事件。
watch: {
$route: {
handler() {
timer = setTimeout(() => {
this.itemBindEvent();
}, 0);
},
immediate: true
}
},
itemBindEvent() {
this.$nextTick(() => {
// 获取到所有的tab节点
let tabsItem = document.body.querySelectorAll(".el-tabs__header .el-tabs__item");
// 给每个节点绑定对应的事件
tabsItem.forEach(item => {
item.addEventListener("contextmenu", (event) => {
// 阻止原生的菜单栏显示
event.preventDefault();
// 获取target
let target = event.target;
// 关闭按钮上点击右键找到父节点
if (event.target.className === "el-icon-close") {
target = event.target.parentNode;
}
// 获取tabs对应的模块名和路由名称
let routeName = target.getAttribute("id").split("-")[1];
let name = target.innerText;
// 展开自定义菜单栏
this.openMenu(event);
});
});
});
}
注:
这里需要注意的有2点
- 因为有了
$nextTick
,路由改变获取的节点是操作关闭其他
或者关闭所有
前的节点。会造成获取tabsItem
的集合
中没有新生成的节点
的问题,所以这里加了个延时定时器
去解决。 - 在
el-tab-pane
的关闭按钮
上点击鼠标右键
时需要获取的target
是它的父节点
。
3-2-2. 菜单栏出现的位置
// 打开右键菜单栏
openMenu(e) {
// 最大宽度
const menuMinWidth = 135;
// 当前元素距离浏览左边的距离
const offsetLeft = this.$el.getBoundingClientRect().left;
// 当前元素的宽度
const offsetWidth = this.$el.offsetWidth;
// 设置菜单出现的边界
const maxLeft = offsetWidth - menuMinWidth;
const left = e.clientX - offsetLeft;
if (left > maxLeft) {
this.menuLeft = maxLeft;
} else {
this.menuLeft = left;
}
// 距离顶部的位置的偏移量
this.menuTop = e.clientY - 60;
// 打开菜单栏
this.showMenu = true;
},
// 关闭右键菜单
closeMenu() {
this.showMenu = false;
},
注:
有关getBoundingClientRect
详情请移步getBoundingClientRect
3-3-3. 其他注意点
- 菜单栏显示隐藏的时候需要给
document.body
绑定click
和移除click
事件。
watch: {
showMenu(show) {
if (show) {
document.body.addEventListener("click", this.closeMenu);
} else {
document.body.removeEventListener("click", this.closeMenu);
}
}
},
- 组件销毁时清除定时器。
beforeDestroy() {
clearTimeout(timer);
}
4 结语
以前的实现自定义菜单栏blog
虽然看不到了,但是有被收录到JavaScript中文网
社区,是另外的一种实现方法vue自定义右键菜单
最后,喜欢的话请点个赞呗❤️❤️。