必须调用e.preventDefault()才能阻止右键菜单,仅return false在现代浏览器中失效;需全局/局部监听并加守卫条件,移动端长按无法拦截,前端禁右键仅为弱提示,敏感内容须服务端防护。

oncontextmenu 事件必须调用 e.preventDefault() 才能阻止系统菜单
只写 oncontextmenu="return false" 或在监听器里漏掉 e.preventDefault(),系统右键菜单照样弹出。这是因为浏览器默认行为不会被 return false 自动取消——尤其在现代浏览器(如 Brave、新版 Chrome)中,该写法已基本失效。
常见错误包括:
- 在 Vue/React 中写
@contextmenu="() => false"或onContextMenu={() => false},框架不解析 return false 的语义,必须显式调用e.preventDefault() - 监听器里用了
setTimeout或异步逻辑包裹e.preventDefault(),导致执行延迟,菜单已弹出 - 绑定在父容器却没检查
e.target,结果连<input>的粘贴菜单也被拦了
绑定位置不对,oncontextmenu 就会漏拦截
内联写法 oncontextmenu="return false" 只对当前元素及其子元素生效,<iframe></iframe> 内部、Shadow DOM 里的节点、或动态插入的元素全都不受控。更糟的是,它会覆盖其他通过 addEventListener 注册的同类型监听器,造成冲突。
推荐做法:
立即学习“前端免费学习笔记(深入)”;
- 全局拦截:用
document.addEventListener('contextmenu', e => e.preventDefault()),但加守卫条件,比如if (!e.target.matches('input, textarea, select')) e.preventDefault() - 局部拦截:直接绑定到目标区域,如
document.getElementById('chart-area').addEventListener('contextmenu', e => e.preventDefault()) - 动态元素:用事件委托,监听父容器的
contextmenu,再根据e.target判断是否需要拦截
移动端长按不是 contextmenu,JS 拦不住
iOS Safari 和 Android Chrome 中,长按触发的是系统级菜单(“保存图片”“复制”等),根本不派发标准 contextmenu 事件。此时 e.preventDefault() 完全无效。
能做的只有弱化提示:
- 给图片加
draggable="false" - CSS 加三行:
-webkit-touch-callout: none;、user-select: none;、-webkit-user-drag: none; - 监听
touchstart并e.preventDefault()有一定抑制效果,但会干扰正常滚动和点击,慎用
禁用右键 ≠ 防止资源获取,这点最容易被忽略
无论你怎么拦 contextmenu,用户都能通过禁用 JS、Shift+F10、开发者工具查看源码、抓包下载图片或接口数据。前端拦截只是视觉层弱提示,敏感内容必须由服务端控制权限、水印、分片加载或动态 token 校验。
真正要防的不是右键,而是资源裸露路径和未授权访问——把精力花在 Content-Security-Policy 头、接口鉴权、图片防盗链配置上,比反复调试 oncontextmenu 实际得多。



















