You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 21:06:02