用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';
   // 上述完整代码放这
})();
相关推荐
十八朵郁金香42 分钟前
【JavaScript】深入理解模块化
开发语言·javascript·ecmascript
m0_748230941 小时前
Redis 通用命令
前端·redis·bootstrap
YaHuiLiang1 小时前
一切的根本都是前端“娱乐圈化”
前端·javascript·代码规范
菜鸟一枚在这2 小时前
深入解析设计模式之单例模式
开发语言·javascript·单例模式
ObjectX前端实验室2 小时前
个人网站开发记录-引流公众号 & 谷歌分析 & 谷歌广告 & GTM
前端·程序员·开源
CL_IN3 小时前
企业数据集成:实现高效调拨出库自动化
java·前端·自动化
浪九天4 小时前
Vue 不同大版本与 Node.js 版本匹配的详细参数
前端·vue.js·node.js
qianmoQ4 小时前
第五章:工程化实践 - 第三节 - Tailwind CSS 大型项目最佳实践
前端·css
C#Thread4 小时前
C#上位机--流程控制(IF语句)
开发语言·javascript·ecmascript
椰果uu5 小时前
前端八股万文总结——JS+ES6
前端·javascript·es6