用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';
   // 上述完整代码放这
})();
相关推荐
VOLUN3 分钟前
PageLayout布局组件封装技巧
前端·javascript·vue.js
掘金安东尼3 分钟前
React 的 use() API 或将取代 useContext
前端·javascript·react.js
牛马喜喜4 分钟前
记录一次el-table+sortablejs的拖拽bug
前端
一枚前端小能手8 分钟前
⚡ Vite开发体验还能更爽?这些配置你试过吗
前端·vite
anyup24 分钟前
🔥 🔥 为什么我建议你使用 uView Pro 来开发 uni-app 项目?
前端·vue.js·uni-app
Skelanimals25 分钟前
Elpis全栈框架开发总结
前端
蓝胖子的小叮当29 分钟前
JavaScript基础(十三)函数柯里化curry
前端·javascript
孪创启航营33 分钟前
数字孪生二维热力图制作,看这篇文章就够了!
前端·three.js·cesium
宫水三叶的刷题日记36 分钟前
真的会玩,钉钉前脚辟谣高管凌晨巡查工位,小编随后深夜发文
前端·后端·面试
zzywxc7871 小时前
AI 行业应用:金融、医疗、教育、制造业领域的落地案例与技术实现
android·前端·人工智能·chrome·金融·rxjava