
网页右键被禁?前端老油条教你3招防偷图(附避坑指南)
网页右键被禁?前端老油条教你3招防偷图(附避坑指南)
友情提示:本文全程碎碎念,代码管饱,吐槽管饱,阅读时请自备瓜子可乐,别被老板看见你在摸鱼。
老板又拍桌子:把右键给我焊死!
周五下午五点二十九分,还有一分钟就能关机去撸串,产品突然在群里 @ 全员:
“老板说了,官网上的高清原图三天之内被竞品扒了三次,今晚必须把右键禁用上线!”
我盯着屏幕,脑子里飘过一万句脏话——禁右键?这年头连我奶奶都知道 F12 好不好!
可谁让人家是金主爸爸,只得把撸串局鸽了,打开 VSCode 开始撸码。
一边撸一边想:干脆写个长文,把这些年踩过的坑一次性倒出来,下次再有人提“禁右键”,直接把链接甩过去,省得口水。
先别急着写代码,搞清楚“右键”到底是个啥
浏览器里你“哒”一下右键,其实触发的是 contextmenu 事件。
这玩意儿的流程跟点按钮一模一样:捕获 → 目标 → 冒泡。
只要你能在事件流里插一脚,就能把它截胡。
简单粗暴理解:
contextmenu 就是一只到处乱窜的野狗,你提前在路口蹲守,一把拽住狗尾巴,它就到不了终点(系统菜单)。
拽尾巴专业术语叫 event.preventDefault(),别写错了,写成 return false 只在某些 DOM0 级事件里好使,在 addEventListener 里就是摆设。
第一招:原生 JS 一把梭,最省流量
<!-- 别抄完就扔生产,后面有坑 -->
<img id="cat" src="cat-hd.jpg" />
<script>
const cat = document.getElementById('cat');
// 写法 1:最简洁,但别这么写,原因往下看
// cat.oncontextmenu = () => false;
// 写法 2:正规军,记得 {passive: false}
cat.addEventListener('contextmenu', function (e) {
e.preventDefault(); // 拽狗尾巴
// 想耍帅可以加个提示
alert('嘿,哥们,图片有版权,喜欢请去官网购买原图~');
}, { passive: false });
</script>
跑起来看似 OK,实际坑点:
- 动态渲染的图片监听不到
后端返回 JSON,前端 v-for 一把渲染,#cat元素根本不在初始 HTML 里,事件绑个寂寞。 - 事件委托忘写
matches
把监听挂到 document 上,结果整个页面都禁了,用户连输入框里右键粘贴都报废,被骂上热搜别找我哭。
正确姿势:事件委托 + 过滤目标
// 任何时刻插入的图片都能罩住
document.addEventListener('contextmenu', function (e) {
// 只对含 data-no-right 属性的元素下手
if (e.target.closest('[data-no-right]')) {
e.preventDefault();
// 弄个轻量 toast 提示,比 alert 友好
toast('图片受保护,禁止保存');
}
}, { passive: false });
<img data-no-right src="cat-hd.jpg" />
好处:
- 不用管 React/Vue 谁渲染谁,只要是带标记的元素统统拿下;
- 其他区域右键正常,用户还能复制文字、粘贴密码,体验保住了。
第二招:React 里怎么塞才不“反模式”
函数组件里直接写 addEventListener?
可以,但别在组件外头写,否则热更新一次就多绑一次,内存泄露警告⚠️。
import { useEffect } from 'react';
export default function ProtectedImg({ src }: { src: string }) {
useEffect(() => {
// 1. 事件委托,一次绑定,全局生效
const handler = (e: MouseEvent) => {
const t = e.target as HTMLElement;
if (t.dataset?.noRight !== undefined) {
e.preventDefault();
// 用 antd/element-plus 的 Message 都行
console.log('老板不让存图~');
}
};
document.addEventListener('contextmenu', handler, { passive: false });
// 2. 清理函数,组件卸载时解绑,防止重复
return () => document.removeEventListener('contextmenu', handler);
}, []);
return (
<img
data-no-right
src={src}
style={{ maxWidth: '100%', display: 'block' }}
alt=" protected"
/>
);
}
注意:
- 千万别在
onContextMenu={e => e.preventDefault()}里写,移动端长按不会触发onContextMenu,坑不死你。 - 如果项目里用了 Next.js,确保只在
useEffect里访问document,否则会报document is not defined,SSR 翻车现场。
第三招:Vue3 组合式 API,一样丝滑
<template>
<img v-no-right :src="src" />
</template>
<script setup lang="ts">
// 自定义指令,哪里需要贴哪里
const vNoRight = {
mounted(el: HTMLElement) {
el.dataset.noRight = '';
},
};
</script>
全局事件委托还是写在 App.vue 里最舒服,只绑一次,比你在每个 <img> 上挂监听省内存多了。
jQuery 古董?别笑,还真有人在用
十年前的老后台,页面里连 webpack 都没有,只能 script 引入 jquery.min.js。
那也别嫌弃,一行代码照样能跑:
$(document).on('contextmenu', '[data-no-right]', function (e) {
e.preventDefault();
$.messager.show('提示', '图片已加水印,请支持正版', 2000);
});
提示:
- 如果页面里同时引了
jquery.js和jquery.min.js,事件会绑两次,用户点一次右键弹两次提示,社死现场。 - 老项目大概率没开 CSP,inline 事件处理器 (
<img oncontextmenu="return false">) 能用,但维护性为零,后人会骂你。
以为禁右键就万事大吉?截图党教你做人
- F12 → Elements → 图片链接复制 → 新标签打开 → 右键另存为
你的contextmenu连根毛都碰不到。 - Windows + Shift + S 直接区域截图,一秒到手,高清无码。
- 手机端长按照片 → 识别二维码 → 保存图片,你拦得住?
- 装个浏览器插件,在资源请求阶段就拦截响应,把图片自动下载到本地文件夹,你连人家什么时候偷的都不知道。
结论:
技术只能防“君子”,真想偷的人你拦不住。
老板如果听不懂人话,就把上面四条甩给他,让他自己选:是要用户体验,还是要伪安全感。
可有些场景确实得禁,怎么办?
-
在线考试系统
题目是花 30 万买的题库,真·不能外泄。
这时候光禁右键还不够,必须组合拳:- 前端禁用选中文本
user-select: none; - 图片用 CSS 背景图
background-image,屏蔽拖拽; - 水印 + 低分辨率 + 接口带 token 鉴权,10 分钟过期;
- 后端记录截图行为,检测到同 IP 高频请求直接封号。
- 前端禁用选中文本
-
内部 OA 防误操作
财务小姐姐看报表,一不小心右键另存把敏感数据发错群,禁右键能减少 80% 手滑。
这种场景用户不会骂你,因为他们也怕背锅。
用户骂“右键不能用好烦”,试试替代方案
- 自定义右键菜单
既能把“保存”按钮藏起来,又能塞“举报盗版”入口,政治正确拉满。
document.addEventListener('contextmenu', function (e) {
if (!e.target.closest('.img-box')) return;
e.preventDefault();
const menu = document.querySelector('#custom-menu');
menu.style.left = e.pageX + 'px';
menu.style.top = e.pageY + 'px';
menu.classList.remove('hidden');
});
#custom-menu {
position: absolute;
background: #fff;
border: 1px solid #ddd;
border-radius: 6px;
padding: 8px 0;
box-shadow: 0 2px 12px rgba(0,0,0,.15);
}
#custom-menu li {
padding: 6px 20px;
list-style: none;
cursor: pointer;
}
#custom-menu li:hover {
background: #f5f5f5;
}
<ul id="custom-menu" class="hidden">
<li>查看原图(带水印)</li>
<li>购买高清版</li>
<li>举报盗版</li>
</ul>
- 低清预览 + 付费高清
页面里放 600px 的压缩图,右键随便他存,真要高清?扫码付款后给 4K 原图。
用户体验 + 营收双杀,老板看了都点赞。
代码写了却不生效?99% 踩了这些坑
| 坑点 | 现场翻车 | 正确姿势 |
|---|---|---|
| 事件冒泡没阻止 | 父级元素又监听了 contextmenu 并 return true | 全程 e.preventDefault() + stopPropagation() |
return false 混用 | addEventListener 里写 return false 毫无卵用 | 记住:只有 DOM0 级 oncontextmenu="return false" 才认 |
| SSR 渲染不一致 | Next.js 报错 document is not defined | 事件相关代码包在 useEffect 里,确保只在客户端执行 |
| 移动端不触发 | 监听 contextmenu 却发现手机长按没反应 | 移动端要监听 touchstart + 长按计时,别一棵树上吊死 |
再来点骚操作,让你面试能吹水
-
只禁特定区域,别全局封杀
文章正文要复制粘贴,评论区可以禁,用closest过滤元素,精准打击。 -
检测移动端自动绕过
手机端反正也防不住,不如直接躺平,还能省一次preventDefault:
const isMobile = /android|iphone|ipad/i.test(navigator.userAgent);
if (!isMobile) {
document.addEventListener('contextmenu', handler, { passive: false });
}
-
CSS 连文字一起锁
user-select: none; -webkit-user-select: none; -moz-user-select: none;
注意只对图片容器加,全局加用户想复制标题都会骂娘。 -
水印动态画在 Canvas 上
后端原图不带水印,前端 Canvas 实时绘制,截图党拿到也带水印,追溯源头美滋滋:
function addWatermark(canvas, text) {
const ctx = canvas.getContext('2d');
ctx.font = '24px serif';
ctx.fillStyle = 'rgba(255,0,0,.5)';
ctx.rotate(-0.2);
ctx.fillText(text, 50, 100);
}
收尾吐槽:技术防君子不防小人,别钻牛角尖
写到这儿,凌晨一点半,我外卖点的烤冷面都坨了。
回头想想,前端能做的,只是让“伸手党”多按一次键,让“误操作党”少背一次锅。
真想盗版的人,相机怼屏幕都能拍,你还想怎样?
所以,下次老板再拍桌子“右键必须禁”,先递这篇长文,再递一份替代方案报价单:
“老板,禁右键 0 元,但用户体验估值 30 万,您选哪个?”
他要是还执迷不悟?
把 F12 截图甩他脸上,让他社死一次,
世界就清净了。
——全文完,祝各位周五不加班,右键永自由。
欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。
推荐:DTcode7的博客首页。
一个做过前端开发的产品经理,经历过睿智产品的折磨导致脱发之后,励志要翻身农奴把歌唱,一边打入敌人内部一边持续提升自己,为我们广大开发同胞谋福祉,坚决抵制睿智产品折磨我们码农兄弟!
| 专栏系列(点击解锁) | 学习路线(点击解锁) | 知识定位 |
|---|---|---|
| 《微信小程序相关博客》 | 持续更新中~ | 结合微信官方原生框架、uniapp等小程序框架,记录请求、封装、tabbar、UI组件的学习记录和使用技巧等 |
| 《AIGC相关博客》 | 持续更新中~ | AIGC、AI生产力工具的介绍,例如stable diffusion这种的AI绘画工具安装、使用、技巧等总结 |
| 《HTML网站开发相关》 | 《前端基础入门三大核心之html相关博客》 | 前端基础入门三大核心之html板块的内容,入坑前端或者辅助学习的必看知识 |
| 《前端基础入门三大核心之JS相关博客》 | 前端JS是JavaScript语言在网页开发中的应用,负责实现交互效果和动态内容。它与HTML和CSS并称前端三剑客,共同构建用户界面。 通过操作DOM元素、响应事件、发起网络请求等,JS使页面能够响应用户行为,实现数据动态展示和页面流畅跳转,是现代Web开发的核心 | |
| 《前端基础入门三大核心之CSS相关博客》 | 介绍前端开发中遇到的CSS疑问和各种奇妙的CSS语法,同时收集精美的CSS效果代码,用来丰富你的web网页 | |
| 《canvas绘图相关博客》 | Canvas是HTML5中用于绘制图形的元素,通过JavaScript及其提供的绘图API,开发者可以在网页上绘制出各种复杂的图形、动画和图像效果。Canvas提供了高度的灵活性和控制力,使得前端绘图技术更加丰富和多样化 | |
| 《Vue实战相关博客》 | 持续更新中~ | 详细总结了常用UI库elementUI的使用技巧以及Vue的学习之旅 |
| 《python相关博客》 | 持续更新中~ | Python,简洁易学的编程语言,强大到足以应对各种应用场景,是编程新手的理想选择,也是专业人士的得力工具 |
| 《sql数据库相关博客》 | 持续更新中~ | SQL数据库:高效管理数据的利器,学会SQL,轻松驾驭结构化数据,解锁数据分析与挖掘的无限可能 |
| 《算法系列相关博客》 | 持续更新中~ | 算法与数据结构学习总结,通过JS来编写处理复杂有趣的算法问题,提升你的技术思维 |
| 《IT信息技术相关博客》 | 持续更新中~ | 作为信息化人员所需要掌握的底层技术,涉及软件开发、网络建设、系统维护等领域的知识 |
| 《信息化人员基础技能知识相关博客》 | 无论你是开发、产品、实施、经理,只要是从事信息化相关行业的人员,都应该掌握这些信息化的基础知识,可以不精通但是一定要了解,避免日常工作中贻笑大方 | |
| 《信息化技能面试宝典相关博客》 | 涉及信息化相关工作基础知识和面试技巧,提升自我能力与面试通过率,扩展知识面 | |
| 《前端开发习惯与小技巧相关博客》 | 持续更新中~ | 罗列常用的开发工具使用技巧,如 Vscode快捷键操作、Git、CMD、游览器控制台等 |
| 《photoshop相关博客》 | 持续更新中~ | 基础的PS学习记录,含括PPI与DPI、物理像素dp、逻辑像素dip、矢量图和位图以及帧动画等的学习总结 |
| 日常开发&办公&生产【实用工具】分享相关博客》 | 持续更新中~ | 分享介绍各种开发中、工作中、个人生产以及学习上的工具,丰富阅历,给大家提供处理事情的更多角度,学习了解更多的便利工具,如Fiddler抓包、办公快捷键、虚拟机VMware等工具 |
吾辈才疏学浅,摹写之作,恐有瑕疵。望诸君海涵赐教。望轻喷,嘤嘤嘤
非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。愿斯文对汝有所裨益,纵其简陋未及渊博,亦足以略尽绵薄之力。倘若尚存阙漏,敬请不吝斧正,俾便精进!

&spm=1001.2101.3001.5002&articleId=156736179&d=1&t=3&u=8d8a56457c8047a884ebb11d1bf7e6cd)
3540

被折叠的 条评论
为什么被折叠?



