网页右键被禁?前端老油条教你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,实际坑点:

  1. 动态渲染的图片监听不到
    后端返回 JSON,前端 v-for 一把渲染,#cat 元素根本不在初始 HTML 里,事件绑个寂寞。
  2. 事件委托忘写 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.jsjquery.min.js事件会绑两次,用户点一次右键弹两次提示,社死现场
  • 老项目大概率没开 CSP,inline 事件处理器 (<img oncontextmenu="return false">) 能用,但维护性为零,后人会骂你。

以为禁右键就万事大吉?截图党教你做人

  1. F12 → Elements → 图片链接复制 → 新标签打开 → 右键另存为
    你的 contextmenu 连根毛都碰不到。
  2. Windows + Shift + S 直接区域截图,一秒到手,高清无码。
  3. 手机端长按照片 → 识别二维码 → 保存图片,你拦得住?
  4. 装个浏览器插件,在资源请求阶段就拦截响应,把图片自动下载到本地文件夹,你连人家什么时候偷的都不知道。

结论:
技术只能防“君子”,真想偷的人你拦不住
老板如果听不懂人话,就把上面四条甩给他,让他自己选:是要用户体验,还是要伪安全感


可有些场景确实得禁,怎么办?

  1. 在线考试系统
    题目是花 30 万买的题库,真·不能外泄
    这时候光禁右键还不够,必须组合拳

    • 前端禁用选中文本 user-select: none
    • 图片用 CSS 背景图 background-image屏蔽拖拽
    • 水印 + 低分辨率 + 接口带 token 鉴权,10 分钟过期
    • 后端记录截图行为,检测到同 IP 高频请求直接封号
  2. 内部 OA 防误操作
    财务小姐姐看报表,一不小心右键另存把敏感数据发错群,禁右键能减少 80% 手滑。
    这种场景用户不会骂你,因为他们也怕背锅。


用户骂“右键不能用好烦”,试试替代方案

  1. 自定义右键菜单
    既能把“保存”按钮藏起来,又能塞“举报盗版”入口,政治正确拉满
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>
  1. 低清预览 + 付费高清
    页面里放 600px 的压缩图,右键随便他存,真要高清?扫码付款后给 4K 原图。
    用户体验 + 营收双杀,老板看了都点赞。

代码写了却不生效?99% 踩了这些坑

坑点现场翻车正确姿势
事件冒泡没阻止父级元素又监听了 contextmenureturn true全程 e.preventDefault() + stopPropagation()
return false 混用addEventListener 里写 return false 毫无卵用记住:只有 DOM0 级 oncontextmenu="return false" 才认
SSR 渲染不一致Next.js 报错 document is not defined事件相关代码包在 useEffect 里,确保只在客户端执行
移动端不触发监听 contextmenu 却发现手机长按没反应移动端要监听 touchstart + 长按计时,别一棵树上吊死

再来点骚操作,让你面试能吹水

  1. 只禁特定区域,别全局封杀
    文章正文要复制粘贴,评论区可以禁,用 closest 过滤元素,精准打击

  2. 检测移动端自动绕过
    手机端反正也防不住,不如直接躺平,还能省一次 preventDefault

const isMobile = /android|iphone|ipad/i.test(navigator.userAgent);
if (!isMobile) {
  document.addEventListener('contextmenu', handler, { passive: false });
}
  1. CSS 连文字一起锁
    user-select: none; -webkit-user-select: none; -moz-user-select: none;
    注意只对图片容器加,全局加用户想复制标题都会骂娘。

  2. 水印动态画在 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等工具

吾辈才疏学浅,摹写之作,恐有瑕疵。望诸君海涵赐教。望轻喷,嘤嘤嘤

非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。愿斯文对汝有所裨益,纵其简陋未及渊博,亦足以略尽绵薄之力。倘若尚存阙漏,敬请不吝斧正,俾便精进!

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DTcode7

客官,赏个铜板吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值