用js脚本移除网页的复制监听

当我想在网页复制点内容,ctrl + c 时被要求告知需要加入vip,需要登陆,完了还得在我复制得内容后面随意拉屎。

对于这样的流氓行为,我不想惯着它了,搞它。

搞它前,先要知道,网页是怎么知道我复制内容了的?

js是运行在浏览器的唯一脚本语言,为了实现网页与用户的各种交互行为,实现了n多事件,最为常见的比如click点击事件。这些众多事件中,就有一个copy事件,用来监听用户的复制行为,比如执行ctrl + c 或是 右键复制。

现在知道了copy,那要阻止流氓行为,只要让网页中的copy事件失效就可以了。问题是,怎么才能让它失效?

到这里,先搞清楚网页是如何绑定一个事件的?

一般来说绑定事件有 三种常见形式:

  • 使用on+事件名称直接绑定

  • 使用addEventListener函数绑定

  • 使用行内属性进行绑定

第一种,使用on+事件名称直接绑定

该如何处理,先来一段代码:

js 复制代码
<body>  
     <div class="copy-box">  
          <p>text content</p>  
     </div>  
</body>

上述代码中,得到一个这样的结构:body -> div.copy-box -> p。

我们分别在div.copy-box 、document、window上绑定一个click事件,如下:

js 复制代码
const ev = 'onclick'  
document.querySelector('.copy-box')[ev] = () => console.log('dom event: ' + ev)  
document[ev] = () => console.log('document event: ' + ev)  
window[ev] = () => console.log('window event: ' + ev)

根据js的事件冒泡机制,事件会从实际触发的节点依次向上冒泡。在上述代码里,window对象包含了整个网页的所有内容。document对象就是文档对象,包含了网页了所有节点信息,document也是window下的一个对象。根据包含关系得到以下结构:window -> document -> html ->body -> div.copy-box -> p。所以在上述代码执行时,会依次输出dom event: onclick、document event: onclick、window event: onclick。

敏锐一点,可以发现,事件的绑定都是在各自的最后出现ev,ev是一个变量,值是:onclick。在不使用变量的情况下, 就是onclick。这就是js 对象数据类型的经典使用方式。
[ev] === .onclick. 这样完全可以把上述代码中的document.querySelector('.copy-box')、document、window看成对象结构,onclick就是这个对象中的某一个键,后面运行的函数就是值。

既然是对象,那么是不是只要让这个对象的键onclick不被修改就可以了。是的, 想要锁定对象的值不能被修改,就得寄出 Object.defineProperty。

defineProperty的基本语法:Object.defineProperty(对象, 键, {value, writable, enumerable、configurable})

要实现禁止对象的属性值被修改,只需关注writable 即可,得到以下代码:

js 复制代码
const o = { value: null, writable: false }  
Object.defineProperty(HTMLElement.prototype, ev, o)  
Object.defineProperty(Document.prototype, ev, o)  
Object.defineProperty(window, ev, o)

让这段代码放在事件绑定之前运行,在页面再次点击div.copy-box,会发现绑定的事件不会在触发,达到了禁止的目的。

再来看这二种事件绑定的形式:使用addEventListener函数绑定。

js 复制代码
const ev = 'click'
document.querySelector('.copy-box').addEventListener(ev, () => console.log('dom event: ' + ev)) 
document.addEventListener(ev, () => console.log('document event: ' + ev))  
window.addEventListener(ev, () => console.log('window event: ' + ev))

我们发现,这时的事件绑定都使用了addEventListener函数,事件通过函数参数进行绑定,没有了之前的类似对象的键值关系,之前的禁止代码对于addEventListener失去了作用。但是,addEventListener是函数,那是不是可以提前篡改这个函数?

js 复制代码
HTMLElement.prototype._addEventListener = Element.prototype.addEventListener;
Document.prototype._addEventListener = Document.prototype.addEventListener;
Window.prototype._addEventListener = Window.prototype.addEventListener;
HTMLElement.prototype.addEventListener = _xaddEventListener;
Document.prototype.addEventListener = _xaddEventListener;
Window.prototype.addEventListener = _xaddEventListener;
function _xaddEventListener(a, b, c) {
  if (a != ev) this._addEventListener(a, b, c);
};

这段代码中,我们在各自的原型链找到addEventListener,同时写一个自定义的_addEventListener函数,先将原本的addEventListener赋值给它。接着开始篡改原始addEventListener

函数,篡改的函数里对传入的事件进行判断,识别到click,直接丢弃不在处理。这是再次点击div.copy-box,会发现绑定的事件也不会在触发了,再次达到了禁止的目的。

最后再来搞定第三种事件绑定形式:使用行内属性进行绑定。

js 复制代码
// html  
html  ->  onclick="clickfn('html')"  
body -> onclick="clickfn('body')"
div -> onclick="clickfn('dom')"

上述代码中,我们在div、body、html处分别绑定了click事件。要达到事件失效,只需要干掉这个onclick属性即可。

js 复制代码
window.addEventListener('DOMContentLoaded', function() {
  const clicks = [...document.querySelectorAll('[' + onev + ']')]
  clicks.forEach(item => {
    item.removeAttribute(onev)
    item.removeAttribute(onev)
  })
})

DOMContentLoaded

MDN 的解释:当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完成加载。

上述代码里,我们将逻辑处理放到了事件DOMContentLoaded里运行,为了干掉所有的onclick属性,使用document.querySelectorAll来搜索。循环移除。

至此,事件的禁止就ok了。

贴上一份完整代码:

js 复制代码
const ev = 'copy'
const onev = 'on' + ev
const o = { value: null, writable: false }
Object.defineProperty(HTMLElement.prototype, onev, o)
Object.defineProperty(Document.prototype, onev, o)
Object.defineProperty(window, onev, o)

HTMLElement.prototype._addEventListener = Element.prototype.addEventListener;
Document.prototype._addEventListener = Document.prototype.addEventListener;
Window.prototype._addEventListener = Window.prototype.addEventListener;
HTMLElement.prototype.addEventListener = _xaddEventListener;
Document.prototype.addEventListener = _xaddEventListener;
Window.prototype.addEventListener = _xaddEventListener;
function _xaddEventListener(a, b, c) {
  if (a != ev) this._addEventListener(a, b, c);
};

window.addEventListener('DOMContentLoaded', function() {
  const clicks = [...document.querySelectorAll('[' + onev + ']')]
  clicks.forEach(item => {
    item.removeAttribute(onev)
    item.removeAttribute(onev)
  })
})

这么让它在所有网页运行,篡改猴 你值得拥有。

js 复制代码
// ==UserScript==
// @name         移除所有网页绑定的copy函数,实现自由复制
// @version      1.0
// @description  try to take over the world!
// @author       -
// @match        *://*/*
// @icon         https://g.csdnimg.cn/static/logo/favicon32.ico
// @grant        none
// @run-at document-start
// ==/UserScript==

(function() {
  'use strict';
   // 上述完整代码放这
})();
相关推荐
excel12 分钟前
基于 GPT 的自动发布文章接口方案:从一句话或一张图片生成并发布内容
前端·javascript·后端
其实防守也摸鱼2 小时前
个人学习 Pwn 思路:从基础到漏洞利用的进阶路径
大数据·前端·人工智能·chrome·算法·安全·网络安全
朱涛的自习室3 小时前
212 个需求、22 万行代码:Agent 接管开源项目的 19 天
android·前端·人工智能
aZhe的全栈知识分享3 小时前
AI 还原 Figma 效果总达不到预期?先别急着让它写代码
前端·后端·面试
review445433 小时前
Codex Hooks 入门实战:生命周期事件、matcher 与5分钟跑通一个钩子
前端·agent·vibecoding
函数小陈3 小时前
阿里云部署DSH与RAM完成运维
前端·后端
用户499335025073 小时前
JS 作用域劫持的死穴:动态 this 穿透,以及 AST 怎么救
前端
IT_陈寒3 小时前
React的setState真是个演员,你以为它同步其实在装睡
前端·人工智能·后端
孟健5 小时前
Claude Code 同需求写两版对比:装不装 frontend-design 差多远?
前端·ai编程·claude
OpsEye6 小时前
大模型 Token 计量不准,是什么原因,怎么解决??
javascript·ai编程