弹出菜单应该具有的功能,当鼠标点击其他区域时,则关闭该菜单。
问题来了,怎么检测鼠标点击了其他区域而不是当前菜单?
百度“JS检测区域外的点击事件”,会发现有很多方法,有递归检测父元素,有遍历冒泡节点等等。
vue 解决的思路:将菜单区域的点击事件用 stop 修饰,在最顶层元素调用鼠标单击事件关闭菜单,完毕!
由于菜单区域单击事件已被 stop 停止冒泡,顶层元素无法捕获,因此菜单区域的单击并不会关闭自己。这样,不需要增加任何逻辑代码,只需要在单击事件后增加一个 .stop 后缀来修饰就能解决。
演示效果和源码如下:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.14/vue.min.js"></script>
<title>弹出菜单测试1</title>
<style>
body {padding: 0; margin: 0;}
#base {
width: 100%;
height: 100%;
position: absolute;
}
#nav { margin: 16px;}
#nav>span {
display: inline-block;
border: 1px solid #bbb;
background-color: #ebf4f6;
border-radius: 6px;
padding: 6px 18px;
user-select: none;
}
#nav>span:hover {background-color: #ace4eb;}
.menu>div {
display: flex;
justify-content: flex-start;
flex-direction: column;
width: 160px;
border: 1px solid #bbb;
border-radius: 6px;
padding: 12px 0;
box-shadow: 0px 0px 5px #999;
position: absolute;
}
.menu>div>div {
width: 148px;
height: 32px;
margin: 0 6px;
line-height: 32px;
border-radius: 4px;
text-align: center;
}
.menu>div>div:hover {background-color: #ace4eb;}
hr {
width: 154px;
height: 1px;
color: #808080;
margin: 5px 2px;
}
</style>
</head>
<body>
<div id="base" @click="pickOther">
<div id="nav" @click.stop="showMenu">
<span id="ck1">手机</span>
<span id="ck2">电脑</span>
<span id="ck3">家具</span>
</div>
<div @click.stop="pickMenu" class="menu">
<div v-if="cMenu=='ck1m'" id="ck1m">
<div>手机通讯</div>
<div>手机配件</div>
<div>运营商</div>
<div>智能设备</div>
</div>
<div v-if="cMenu=='ck2m'" id="ck2m">
<div>电脑整机</div>
<div>电脑配件</div>
<hr>
<div>外设产品</div>
<div>游戏设备</div>
<div>游戏设备</div>
<div>办公设备</div>
</div>
<div v-if="cMenu=='ck3m'" id="ck3m">
<div>家电厨具</div>
<div>家装软饰</div>
<div>生活日用</div>
<div>灯具五金</div>
</div>
</div>
</div>
<script>
var vm = new Vue({
el: '#base',
data: { cMenu: '' }, // cMenu 为空时不显示任何菜单
methods: {
showMenu: function () {
const btn = event.target;
if (btn.nodeName.toUpperCase() != 'SPAN') return;
// 计算菜单的新坐标
const e = $(btn);
const x = e.offset().left + e.innerWidth();
const y = e.offset().top + e.innerHeight();
// 弹出菜单跟随按钮位置而移动,这里需要二次渲染(1 显示菜单,2 移动菜单)
const id = btn.id + 'm';
this.cMenu = id;
this.$nextTick(e => { $('#' + id).css({ "top": y, "left": x }); });
},
// 鼠标点击全部区域,菜单区域的单击被阻断冒泡不会触发此函数
pickOther: function () { this.cMenu = ''; },
// 点击菜单区域时,给出当前点击的菜单名
pickMenu: function () { console.log($(event.target).text()); }
}
});
</script>
</body>
</html>