实现的核心在于两点:
- tabindex 属性:使菜单元素能够接收焦点
- blur 事件:当菜单失去焦点时自动关闭
优势在于:
- 代码简洁,逻辑清晰
- 页面中存在多个右键菜单时不需要来回进行开关操作
- 无需复杂的 DOM 事件监听
- 符合用户直觉(点击其他地方菜单消失)
代码实例:
<template>
<div @contextmenu.prevent="openContextMenu">
<!-- 页面内容 -->
<div>右键点击此处显示菜单</div>
<!-- 右键菜单 -->
<div v-if="showMenu" ref="menuRef" tabindex="-1" class="context-menu"
:style="{ left: `${position.x}px`, top: `${position.y}px` }" @blur="showMenu = false">
<div class="menu-item" @click="handleAction('copy')">复制</div>
<div class="menu-item" @click="handleAction('paste')">粘贴</div>
<div class="menu-item" @click="handleAction('delete')">删除</div>
</div>
</div>
</template>
<script setup>
import { ref, nextTick } from 'vue';
const showMenu = ref(false);
const position = ref({ x: 0, y: 0 });
const menuRef = ref(null);
const openContextMenu = (e) => {
showMenu.value = true;
position.value = { x: e.clientX, y: e.clientY };
nextTick(() => {
menuRef.value?.focus();
});
};
const handleAction = (action) => {
console.log('执行操作:', action);
};
</script>
<style>
.context-menu {
position: fixed;
background: white;
border: 1px solid #ddd;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
z-index: 1000;
min-width: 120px;
outline: none;
/* 移除焦点时的轮廓 */
}
.menu-item {
padding: 8px 12px;
cursor: pointer;
}
.menu-item:hover {
background: #f5f5f5;
}
</style>