浏览器里右键时会有一个默认的菜单,在我的开源项目中正好有自定义右键菜单的需求,在npm库找了下与之相关的包,发现都是以组件形式实现的,感觉那种做法太过繁琐。
于是,我就想着能不能像vue的内置指令那样,绑定到元素上,在这个元素上右键就能出现右键菜单,这样做就方便很多了。
看了下vue的自定义指令文档后,经过一番折腾,终于实现我的这个想法,本文就跟大家分享下我的实现思路以及过程,欢迎各位感兴趣的开发者阅读本文。
Vue中有很多内置指令,例如:v-if
、v-for
、v-model
,它除了这些内置指令外,还允许我们开发者自己注册指令,来实现我们想实现的效果,对Vue自定义指令不熟悉的开发者可以先看一下文档:自定义指令。
接下来,就跟大家讲一下我的实现思路:
rightClick
oncontextmenu
事件,对组件传过来的值进行处理接下来,就跟大家分享下我的实现过程。
我们先来看看这个组件需要哪些数据才能让其显示在鼠标所点的位置。
display
属性left
、top
属性v-for
来渲染我们在项目中找一个公用组件,确保这个组件会被渲染,在组件的template
中加入下述代码。
<!--右键菜单-->
<div
id="rightMenuDom"
class="right-menu"
:style="{
display: rightMenuStatus,
top: rightMenuTop,
left: rightMenuLeft
}"
>
<ul>
<!--分为2组渲染-->
<li>
<span
v-for="item in rightMenuList"
:key="item.id"
v-show="item.id <= 3"
@click="item.handler"
>{{ item.text }}
</span>
</li>
<li>
<span
v-for="item in rightMenuList"
:key="item.id"
v-show="item.id > 3"
@click="item.handler"
>{{ item.text }}
</span>
</li>
</ul>
</div>
</div>
随后,在组件的mounted
生命周期中添加全局点击事件的监听,目的是在点击任意位置后隐藏右键菜单。
mounted() {
// 监听全局点击事件
document.addEventListener("click", () => {
// 隐藏右键菜单
this.$store.commit("updateRightMenuStatus", {
status: "none",
left: "0px",
top: "0px"
});
});
}
紧接着,在组件的computed
中获取Vuex中定义的数据,用于渲染右键菜单。
computed: {
// 右键菜单显隐状态
rightMenuStatus(): string {
return this.$store.state.rightMenu.status;
},
// 右键菜单距离浏览器顶部高度
rightMenuTop(): string {
return this.$store.state.rightMenu.top;
},
// 右键菜单距离浏览器左边长度
rightMenuLeft(): string {
return this.$store.state.rightMenu.left;
},
// 右键菜单列表内容
rightMenuList(): [] {
return this.$store.state.rightMenu.list;
}
}
最后,给它编写css样式。
// 右键菜单样式
.right-menu {
position: fixed;
left: 0;
top: 0;
width: 166px;
height: auto;
background-color: rgb(242, 242, 242);
border: solid 1px #C2C1C2;
box-shadow: 0 10px 10px #C2C1C2;
display: none;
border-radius: 5px;
ul {
padding: 0;
margin: 0;
font-size: 15px;
li {
list-style: none;
box-sizing: border-box;
padding: 6px 0;
border-bottom: 1px solid rgb(216, 216, 217);
&:nth-child(1) {
padding-top: 2px;
}
&:nth-last-child(1) {
border-bottom: none;
}
span {
display: block;
height: 20px;
line-height: 20px;
padding-left: 16px;
&:hover {
background-color: #0070F5;
cursor: pointer;
color: #FFFFFF;
}
}
}
}
}
在vuex的配置文件中,找到state
属性,添加下述代码。
status
组件的显隐状态top
组件距离浏览器可视区域顶部的距离left
距离浏览器可视区域左边的距离list
组件需要的文本数据和与之对应的事件处理函数rightMenu: {
status: "none",
top: "0px",
left: "0px",
list: []
}
随后在mutations
中添加更新数据的方法。
// 更新右键菜单数据
updateRightMenuStatus(state, menuObj: rightMenuAttribute) {
state.rightMenu.status = menuObj.status;
state.rightMenu.top = menuObj.top;
state.rightMenu.left = menuObj.left;
state.rightMenu.list = menuObj.list;
}
我们在vue的入口文件:main.ts
中,注册一个全局指令rightClick
。
el
为我们绑定指令的元素binding
里包含了指令传过来的参数app.directive("rightClick", (el, binding) => {
});
上面我们注册了一个全局指令,我们需要在它的函数内部为指令所绑定的元素重写其点击事件,处理指令传过来的参数。
commit
更新Vuex中的相关数据,渲染页面 el.oncontextmenu = function(e: MouseEvent) {
const textArray = binding.value.text;
const handlerObj = binding.value.handler;
// 事件处理数组
const handlerArray = [];
// 处理好的右键菜单
const menuList = [];
// 将事件处理函数放入数组中
for (const key in handlerObj) {
handlerArray.push(handlerObj[key]);
}
// 追加右键菜单数据
for (let i = 0; i < textArray.length; i++) {
// 右键菜单对象, 添加名称
const menuObj = {
text: textArray[i],
handler: handlerArray[i],
id: i + 1
};
menuList.push(menuObj);
}
// 鼠标点的坐标
const oX = e.clientX;
const oY = e.clientY;
// 右键菜单出现后的位置
store.commit("updateRightMenuStatus", {
status: "block",
left: oX + "px",
top: oY + "px",
list: menuList
});
return false;
};
完成上述操作后,我们就已经实现了右键自定义菜单的指令,接下来,我们来看看如何在组件中使用我们注册的指令。
在你想要绑定右键菜单的html
元素上添加v-right-click
,如下所示:
<li
class="row-panel"
v-right-click="rightMenuObj"
>
</li>
在组件的data
中定义右键菜单需要的数据,即上面代码中声名的rightMenuObj
。
// 右键菜单对象,菜单内容和处理事件
rightMenuObj: {
text: [
"查看资料",
"复制用户id",
"移除该会话",
"在联系人中查看",
"在单聊窗口中打开",
"会话置顶"
],
handler: {
checkingData() {
console.log("查看资料点击事件");
},
copyId() {
console.log("复制用户id点击事件");
},
removeItem() {
console.log("移除会话点击事件");
},
showContact() {
console.log("在联系人中查看");
},
showSingleChat() {
console.log("在单聊窗口中打开");
},
topConversation() {
console.log("会话置顶");
}
}
}
随后,我们就可以运行看效果了,如下所示,已经成功实现了我们想要的效果。
图片过大,微信无法加载,可点击下方阅读原文进行查看。
本文中演示所用的组件GitHub地址如下:
msg-list.vue
main.ts
main-content.vue
main-content.scss
index.ts
● 【尤大出品】面向未来的前端构建工具 - Vite● 一杯茶的时间,上手 Koa2 + MySQL 开发● 动手实现一个 Koa 框架(万字实战好文)
·END·