必须先调用 event.preventDefault() 阻止默认右键菜单,再监听 contextmenu 事件并动态定位自定义菜单;推荐用 addEventListener 绑定到目标元素或 document,避免 oncontextmenu 内联属性,同时需处理边界、滚动、ESC 关闭及无障碍支持。
contextmenu 事件浏览器原生右键菜单会覆盖自定义逻辑,必须先阻止它。关键不是“监听右键”,而是“在右键触发时取消默认行为”。contextmenu 是唯一可靠的右键事件,mousedown 或 click 的 button === 2 在部分浏览器(如 Safari 移动端)不可靠,且无法阻止默认菜单。
document)绑定 addEventListener('contextmenu', handler)
event.preventDefault(),否则自定义菜单弹出瞬间原生菜单也会闪现oncontextmenu 内联属性——不利于解耦和动态控制document;若仅限某区域(如表格行),绑定到具体容器更安全,避免误拦截其他组件的右键用 div 或 menu 都可以,但语义和可访问性上 menu 更合适;实际开发中多数仍选 div 因为控制自由度高。定位必须用 position: absolute,且不能依赖 top/left 静态值——右键位置是动态的。
position: absolute; z-index: 9999;,并初始 display: none
event.clientX 和 event.clientY 计算坐标,注意要减去滚动偏移:window.scrollX/window.scrollY
fixed 定位——滚动时菜单会悬浮在错误位置菜单点击不关闭是最常见疏漏,用户点完一项后继续右键,旧菜单残留或新菜单叠在上面。关闭动作不能只靠“点击菜单项”,还要覆盖外部点击、ESC 键、以及页面滚动等场景。
click 事件,在回调末尾调用菜单隐藏函数(如 menuEl.style.display = 'none')document 的 click 事件,用 event.target 判断是否点击在菜单外部,是则关闭keydown 监听 Escape 键,直接关闭菜单document.click),否则多次打开菜单会导致重复绑定、内存泄漏contextmenu 在某些元素上不触发不是所有元素默认响应右键事件。disabled 表单控件、contenteditable="false" 区域、以及部分 Web Component 的 shadow DOM 内部,contextmenu 可能被拦截或不冒泡。
立即学习“前端免费学习笔记(深入)”;
pointer-events: none,否则事件根本不会到达input、textarea 等表单元素,需显式添加 oncontextmenu="return false" 或 JS 阻止,因为它们可能有自己的一套上下文逻辑contextmenu 绑定到容器层,而不是渲染后的图形元素(后者可能无事件冒泡路径)shadowRoot 内监听,或使用 composed: true 选项让事件穿透role="menu" + aria-* 属性),这些往往被跳过,结果在复杂页面里一用就错。