用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(对象, 键, {valuewritable, enumerableconfigurable})

要实现禁止对象的属性值被修改,只需关注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';
   // 上述完整代码放这
})();
相关推荐
RaidenLiu3 分钟前
告别陷阱:精通Flutter Signals的生命周期、高级API与调试之道
前端·flutter·前端框架
非凡ghost3 分钟前
HWiNFO(专业系统信息检测工具)
前端·javascript·后端
非凡ghost5 分钟前
FireAlpaca(免费数字绘图软件)
前端·javascript·后端
非凡ghost12 分钟前
Sucrose Wallpaper Engine(动态壁纸管理工具)
前端·javascript·后端
拉不动的猪13 分钟前
为什么不建议项目里用延时器作为规定时间内的业务操作
前端·javascript·vue.js
该用户已不存在20 分钟前
Gemini CLI 扩展,把Nano Banana 搬到终端
前端·后端·ai编程
地方地方22 分钟前
前端踩坑记:解决图片与 Div 换行间隙的隐藏元凶
前端·javascript
炒米233324 分钟前
【Array】数组的方法
javascript
小猫由里香27 分钟前
小程序打开文件(文件流、地址链接)封装
前端
Tzarevich31 分钟前
使用n8n工作流自动化生成每日科技新闻速览:告别信息过载,拥抱智能阅读
前端