说在前面
🎈网页的功能和用途可能各不相同,在传统右键菜单栏中无法满足每个用户的个性化需求。通过自定义右键菜单栏,用户可以根据自己的需求添加、调整和删除菜单选项,以实现个性化定制。通过自定义右键菜单栏,可以为用户提供快速访问常用功能和操作的便捷方式,从而提高用户体验。
效果展示
实现原理
1、oncontextmenu事件了解一下
oncontextmenu 事件在元素中用户右击鼠标时触发并打开上下文菜单。
oncontextmenu是一个DOM事件,它在用户右键点击时触发。可以通过在HTML元素上添加oncontextmenu属性来指定右键菜单的处理函数。
例如,在一个按钮元素上添加oncontextmenu属性:
html
<button oncontextmenu="showContextMenu(event)">右键点击我</button>
在这个示例中,当用户右键点击按钮时,会调用showContextMenu函数,并将事件对象作为参数传递给该函数。
在JavaScript代码中,可以定义showContextMenu函数来处理右键菜单的显示和操作:
javascript
function showContextMenu(event) {
event.preventDefault(); // 阻止默认的右键菜单弹出
// 显示自定义右键菜单
// ...
}
在showContextMenu函数中,通过调用event.preventDefault()方法阻止浏览器默认的右键菜单弹出。然后,可以根据需要执行自定义的逻辑,例如显示自定义的右键菜单。
2、在指定容器元素自定义右键菜单
首先,使用getElementById方法获取绑定右键菜单的DOM元素和右键菜单的容器元素。如果获取失败,则直接返回。
javascript
const dom = document.getElementById(this.domId);
if (!dom) return;
接着,给绑定右键菜单的DOM元素添加oncontextmenu事件处理函数。当用户触发右键点击事件时,首先调用hideAllMenu方法隐藏所有的右键菜单,然后通过event.preventDefault方法禁止默认行为,防止浏览器弹出默认的右键菜单。接下来,计算出鼠标指针相对于文档顶部和左侧的位置,并设置右键菜单的位置和显示状态。
javascript
const that = this;
dom.oncontextmenu = function (e) {
that.hideAllMenu(that.uid);
// 自定义body元素的鼠标事件处理函数
e = e || window.event;
e.preventDefault();
let scrollTop =
document.documentElement.scrollTop ||
document.body.scrollTop; // 获取垂直滚动条位置
let scrollLeft =
document.documentElement.scrollLeft ||
document.body.scrollLeft; // 获取水平滚动条位置
menu.style.display = "block";
menu.style.left = e.clientX + scrollLeft + "px";
menu.style.top = e.clientY + scrollTop + "px";
};
最后,给document对象添加onclick事件处理函数。当用户在其他位置点击鼠标时,调用hideAllMenu方法隐藏所有的右键菜单。
javascript
document.onclick = function () {
that.hideAllMenu();
};
hideAllMenu(id) {
const jMenu = document.getElementsByClassName("j-mouse-menu");
for (let i = 0; i < jMenu.length; i++) {
if (jMenu[i].id != id) jMenu[i].style.display = "none";
}
},
3、封装成一个组件
(1)template菜单模板
html
<div :id="uid" class="j-mouse-menu">
<slot name="header"></slot>
<ul>
<li
v-for="menuItem in menu"
:key="menuItem.id"
@click="menuClick(menuItem)"
>
{{ menuItem.label }}
</li>
</ul>
<slot name="body"></slot>
<slot name="footer"></slot>
</div>
使用:id="uid"
绑定了组件的id属性,该属性值由组件实例的uid属性提供。这样可以确保每个组件实例都有唯一的id。
然后,给组件添加了j-mouse-menu
类,用于设置组件的样式。
在组件的内容区域中,使用了Vue的插槽机制。
<slot name="header"></slot>
定义了一个名为"header"
的插槽,用于放置菜单栏的头部内容。<ul>
标签下使用了v-for
指令遍历menu
数组,生成菜单项。- 菜单项使用
<li>
标签表示,并通过:key
绑定了唯一的menuItem.id
作为key值。 - 通过
@click
绑定了menuClick
方法,该方法会在点击菜单项时被调用。 - 菜单项的显示文本使用插值语法
{{ menuItem.label }}
来动态显示。
接下来,又定义了两个插槽:
<slot name="body"></slot>
用于放置菜单栏的主体内容。<slot name="footer"></slot>
用于放置菜单栏的底部内容。
(2)props入参
javascript
props: {
domId: {
type: String,
default: "",
},
menu: {
type: Array,
default: () => {
return [];
},
},
},
domId表示需要绑定右键菜单的DOM元素容器的id,menu表示右键菜单的选项列表,menu数据格式如下:
javaScript
[
{
id: "1",
label: "菜单1"
},
{
id: "2",
label: "菜单2",
click: this.test
},
{
id: "3",
label: "菜单3"
},
{
id: "4",
label: "菜单4"
},
{
id: "5",
label: "菜单5"
}
]
(3)菜单点击回调
javascript
menuClick(item) {
if (item.click) {
item.click(item);
return;
}
this.$emit("menuClick", item);
},
首先判断item对象是否存在click属性。如果存在,则执行item.click(item),并将item作为参数传递给click函数。然后,返回结束方法的执行。
如果item对象不存在click属性,即没有自定义的点击处理函数,那么就通过this.$emit("menuClick", item)语法触发一个名为"menuClick"的自定义事件,并将item作为参数传递给父组件。
(4)完整组件代码
html
<template>
<div>
<div :id="uid" class="j-mouse-menu">
<slot name="header"></slot>
<ul>
<li
v-for="menuItem in menu"
:key="menuItem.id"
@click="menuClick(menuItem)"
>
{{ menuItem.label }}
</li>
</ul>
<slot name="body"></slot>
<slot name="footer"></slot>
</div>
</div>
</template>
<script>
import { getUId } from "../../../utils/strTool";
export default {
name: "JMouseMenu",
props: {
domId: {
type: String,
default: "",
},
menu: {
type: Array,
default: () => {
return [];
},
},
},
data() {
return {
uid: "",
};
},
created() {
this.setUid();
},
mounted() {
this.init();
},
methods: {
setUid() {
this.uid = "j-mouse-menu-" + getUId();
},
init() {
// 自定义鼠标右键菜单栏
const dom = document.getElementById(this.domId);
if (!dom) return;
const menu = document.getElementById(this.uid);
const that = this;
dom.oncontextmenu = function (e) {
that.hideAllMenu(that.uid);
// 自定义body元素的鼠标事件处理函数
e = e || window.event;
e.preventDefault();
let scrollTop =
document.documentElement.scrollTop ||
document.body.scrollTop; // 获取垂直滚动条位置
let scrollLeft =
document.documentElement.scrollLeft ||
document.body.scrollLeft; // 获取水平滚动条位置
menu.style.display = "block";
menu.style.left = e.clientX + scrollLeft + "px";
menu.style.top = e.clientY + scrollTop + "px";
};
// 鼠标点击其他位置时隐藏菜单
document.onclick = function () {
that.hideAllMenu();
};
},
hideAllMenu(id) {
const jMenu = document.getElementsByClassName("j-mouse-menu");
for (let i = 0; i < jMenu.length; i++) {
if (jMenu[i].id != id) jMenu[i].style.display = "none";
}
},
menuClick(item) {
if (item.click) {
item.click(item);
return;
}
this.$emit("menuClick", item);
},
},
};
</script>
<style lang="less" scoped>
.j-mouse-menu {
display: none;
position: absolute;
min-width: 8em;
max-width: 15em;
border: 1px solid #ccc;
background: #eee;
ul {
margin: 5px 0;
padding: 0;
}
li {
height: 30px;
line-height: 30px;
color: #21232e;
font-size: 12px;
text-align: center;
cursor: default;
padding: 0;
margin: 0;
list-style-type: none;
border-bottom: 1px dashed #cecece;
&:hover {
background-color: #cccccc;
}
}
}
</style>
(5)组件使用
html
<j-mouse-menu
:domId="'j-mouse-menu-view-content1'"
:menu="myMenu"
@menuClick="menuClick"
>
<template v-slot:header>
<div class="menu-slot-header">🌝JYeontu</div>
</template>
<template v-slot:footer>
<div class="menu-slot">
🦁🐼
</div>
</template>
</j-mouse-menu>
通过插槽自定义右键菜单的头部和底部内容,菜单列表通过menu
参数传入子组件,并绑定菜单点击事件menuClick
。
javascript
data(){
return {
myMenu: [
{
id: "1",
label: "菜单1"
},
{
id: "2",
label: "菜单2",
click: this.test
},
{
id: "3",
label: "菜单3"
},
{
id: "4",
label: "菜单4"
},
{
id: "5",
label: "菜单5"
}
]
}
},
methods:{
menuClick(menuItem) {
alert("点击了:" + menuItem.label);
},
test(menuItem) {
alert("test-" + menuItem.id);
},
alert(label) {
alert("点击了:" + label);
}
}
组件库
组件文档
目前该组件也已经收录到我的组件库,组件文档地址如下: jyeontu.xyz/jvuewheel/#...
组件内容
组件库中还有许多好玩有趣的组件,如:
- 悬浮按钮
- 评论组件
- 词云
- 瀑布流照片容器
- 视频动态封面
- 3D轮播图
- web桌宠
- 贡献度面板
- 拖拽上传
- 自动补全输入框
- 图片滑块验证
等等......
组件库源码
组件库已开源到gitee,有兴趣的也可以到这里看看:gitee.com/zheng_yongt...
觉得有帮助的可以点个star~
有什么问题或错误可以指出,欢迎pr~
有什么想要实现的组件或想法可以联系我~
公众号
关注公众号『前端也能这么有趣
』,获取更多有趣内容。
发送『组件库
』获取源码
说在后面
🎉 这里是 JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『
前端也能这么有趣
』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。