Vue3 自定义context menu绑定@blur后内部按钮点击事件失效问题
问题根因
- 浏览器事件触发顺序为
mousedown→blur→mouseup→click,点击菜单内按钮时,按下鼠标的瞬间就会触发菜单div的blur事件直接隐藏菜单,等鼠标松开时按钮已不在DOM树中,自然无法触发click事件。 - 点击菜单内部元素时,内部元素会获得焦点,导致父级菜单div失焦触发blur,不符合仅点击外部才关闭的需求。
方案1:延迟blur关闭逻辑(改动最小)
仅修改close方法加少量延迟,等待click事件执行完成后再关闭菜单即可:
close() { // 加100ms延迟,预留click事件执行时间 setTimeout(() => { console.log("closed") this.show = false; this.left = 0; this.top = 0; }, 100) }
该方案无需修改其他业务逻辑,适合快速修复问题。
方案2:改用全局点击区域判断(更稳定可控)
完全抛弃blur事件,通过监听全局点击事件判断点击位置是否在菜单外部,从根源上避免内部交互的焦点冲突问题,是自定义右键菜单的主流实现方案:
修改后完整代码
<template> <div class="context-menu" v-show="show" :style="style" ref="context"> <button @click="handle" class="btn btn-outline-secondary">Create Object</button> </div> </template> <script> import { nextTick } from '@vue/runtime-core'; export default { name: 'ContextMenu', data(){ return { left: 0, top: 0, show: false, } }, computed: { style() { return { top: this.top + 'px', left: this.left + 'px', }; }, }, mounted() { // 绑定全局点击监听 document.addEventListener('click', this.handleOutsideClick) }, beforeUnmount() { // 组件销毁前解绑避免内存泄漏 document.removeEventListener('click', this.handleOutsideClick) }, methods: { handleOutsideClick(e) { if (!this.show) return // 判断点击目标是否在菜单内部 const isClickInside = this.$refs.context.contains(e.target) if (!isClickInside) { this.close() } }, close() { console.log("closed") this.show = false; this.left = 0; this.top = 0; }, open(e) { // 阻止默认右键菜单和事件冒泡,避免刚打开就被全局点击事件关闭 e.preventDefault() e.stopPropagation() this.left = e.pageX || e.clientX; this.top = e.pageY || e.clientY; this.show = true; }, handle(){ console.log("clicked") // 业务逻辑 this.close() } } } </script> <style scoped> .context-menu { position: fixed; background: white; z-index: 999; outline: none; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); cursor: pointer; } </style>
内容的提问来源于stack exchange,提问作者Florent
相关产品推荐
相关产品推荐

