当我想在网页复制点内容,ctrl + c 时被要求告知需要加入vip,需要登陆,完了还得在我复制得内容后面随意拉屎。对于这样的流氓行为,我不想惯着它了,搞它。搞它前,先要知道,网页是怎么知道我复制内容了的?
js是运行在浏览器的唯一脚本语言,为了实现网页与用户的各种交互行为,实现了n多事件,最为常见的比如click点击事件。这些众多事件中,就有一个copy事件,用来监听用户的复制行为,比如执行ctrl + c 或是 右键复制。
<body> <div class="copy-box"> <p>text content</p> </div> </body>上述代码中,得到一个这样的结构:body -> div.copy-box -> p。
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 对象数据类型的经典使用方式。
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,会发现绑定的事件不会在触发,达到了禁止的目的。
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是函数,那是不是可以提前篡改这个函数?
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 。
// html html -> onclick="clickfn('html')" body -> onclick="clickfn('body')" div -> onclick="clickfn('dom')"上述代码中,我们在div、body、html处分别绑定了click事件。要达到事件失效,只需要干掉这个onclick属性即可。
window.addEventListener('DOMContentLoaded', function() { const clicks = [...document.querySelectorAll('[' + onev + ']')] clicks.forEach(item => { item.removeAttribute(onev) item.removeAttribute(onev) }) })备注:DOMContentLoaded
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) }) })这么让它在所有网页运行,**篡改猴** 你值得拥有。
// ==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'; // 上述完整代码放这 })();