浏览器页面功能限制与反限制的斗争
开博两年多,只自己重新以高优先级的样式弄了博客的展示界面(兼容IE6···),之后没有任何博文产出,因为后来才开始知道有响应式布局这个东西,想弄完再写(哈哈哈哈,我是屁事特多的类型),也有点不好意思了。这篇文章基本上是对网上已有的文章做了下整理而已,写的也是大家用的主流方法,所以也没啥新内容,也没有多少技术水平,是本人用以当作技术类文章的Helloworld的练笔测试。好啦,开撸,小弟语文不好,有错误请帮忙订正一下(*@ο@*) 哇~
大伙在看网页时,或多或少都会遇到一些禁止右键、选择、复制、黏贴之类的网站,或者在写一些要限制用户操作的页面时应该也有实现过类似的需求。
首先说说可以实现对应禁止功能的,可以达到目的的事件咯。So Easy~
- 禁止右键菜单方法只有一个: 'contextmenu'
- 禁止选择 'selectstart','select'
- 禁止拖动 'dragstart',
- 禁止复制 有这种 'beforecopy', 'copy' ,'selectstart','select','keydown','mouseup'(不然你选择不让你玩鼠标和键盘也是个思路对不?)
- 哈哈哈哈,俺只是个打杂的,不是什么专业的前端开发工程师,估计还会有遗漏,就先留个来凑凑数哈···
简单,这种需求网上一搜到处都是,网上实现的最简单方法无非也就是在body标签里面,禁止掉对应的事件。
如禁止右键菜单:
<body oncontextmenu="return false"> </body>
所谓你省心大家也省心,破解方法也是So Easy:对body,玩个事件一次过解绑就好了。
(function () { ['contextmenu', 'dragstart', 'mouseup', 'copy', 'beforecopy', 'selectstart', 'select', 'keydown'] .forEach(function (evt) { document.body['on'+evt]=null; }); })();
当然,有些会用事件绑定,这里只讨论一些常规的方案哈。但jQuery这东东,实在是太好用了,不给大家讲shenmegui的基础知识,就是告诉大家,bind绑定的都可以用unbind解绑。不要问我为啥不说$.fn.on什么的,on出现的版本太后了,我还真看过现在还有 1.3.2的jq。
思路还是同上,不过,用jquery的网站大部分绑定在 document那里。这次以禁止右键和禁止选择为栗子
(function () {
$(document).on('contextmenu selectstart', function () {
return false;
});
})();
附上解决方法的话:
(function ($) { ['contextmenu', 'dragstart', 'mouseup', 'copy', 'beforecopy', 'selectstart', 'select', 'keydown'] .forEach(function (evt) { $(document).unbind(evt); }); })(jQuery);
哈哈哈,像我这么写这两段代码太恶劣了,太偷懒了,然后就可以合体出兼容性更好的方案
(function ($) { ['contextmenu', 'dragstart', 'mouseup', 'copy', 'beforecopy', 'selectstart', 'select', 'keydown'] .forEach(function (evt) { document.body['on' + evt] = null; if ($) $(document).unbind(evt); }); })(window.jQuery);
当然,绑定的话,不一定是绑定在body或者document元素。可能是绑在某个div那里,就不恶意卖萌加段代码了。这个时候,我们就要对整个页面元素 document.all 都解绑一次,然后你会发现漏掉了 window 和document 这两个也是可以绑定的,所以必须补上。
灯灯灯灯,大家久等的代码:
(function (window) { var eventArr = ['contextmenu', 'dragstart', 'mouseup', 'copy', 'beforecopy', 'selectstart', 'select', 'keydown']; function runScript(window) { var document = window["document"], $ = window["jQuery"], unbind = function (ele) { eventArr.forEach(function (evt) { ele['on' + evt] = null; if ($) { $(ele).unbind(evt); } }); }; [window, document].forEach(unbind); for (var i = 0, all = document.all, len = all.length; i < len; i++) { var ele = all[i]; if (ele && ele.nodeType === 1) { unbind(ele); } } } runScript(window); })(window);
另外,跑到这里的时候,你会发现在路由器管理页面的右键禁止你是无法解决。 因为用了frameset 和iframe。 所以,在遍历元素的时候,要对frame和iframe 进行操作。同时要反防止跨域的js权限问题,导致后面的元素没跑,也把try catch加上。
1 (function (window) { 2 var eventArr = ['contextmenu', 'dragstart', 'mouseup', 'copy', 'beforecopy', 'selectstart', 'select', 'keydown']; 3 4 function runScript(window) { 5 var document = window["document"], 6 $ = window["jQuery"], 7 unbind = function (ele) { 8 eventArr.forEach(function (evt) { 9 ele['on' + evt] = null; 10 if ($) { 11 $(ele).unbind(evt); 12 } 13 try { 14 if (/frame/i.test(ele.tagName)) { 15 runScript(ele.contentWindow); 16 } 17 } catch (err) { 18 } 19 }); 20 }; 21 [window, document].forEach(unbind); 22 for (var i = 0, all = document.all, len = all.length; i < len; i++) { 23 var ele = all[i]; 24 if (ele && ele.nodeType === 1) { 25 unbind(ele); 26 } 27 } 28 } 29 30 runScript(window); 31 })(window);
然后,有史以来兼容性最强的解决页面功能被禁用的方案终于出炉了。
利用谷歌那啥 js压缩编译器来着,压一下的话共398个字符(唉,我数得可辛苦了/(ㄒoㄒ)/~~)···对应是书签代码也出来了。把这个加到书签里面有事点一点···
javascript:(function(c){function e(a){var b=a.document,f=a.jQuery,g=function(a){h.forEach(function(b){a["on"+b]=null;f&&f(a).unbind(b);try{/frame/i.test(a.tagName)&&e(a.contentWindow)}catch(c){}})};[a,b].forEach(g);a=0;for(var b=b.all,c=b.length;a<c;a++){var d=b[a];d&&1===d.nodeType&&g(d)}}var h="contextmenu dragstart mouseup copy beforecopy selectstart select keydown".split(" ");e(c)})(window);
所以说,禁止页面功能不能用简单方法来实现。如果让我写的话,至少也会实现成这样,看你丫的怎么解绑我原生匿名函数···
(function (window) { function bindEvent(elem, event, handler, useCapture) { event = event.replace(/^on/ig, "").toLowerCase(); if (elem.addEventListener) { elem.addEventListener(event, handler, useCapture); return true; } else if (elem.attachEvent) { return elem.attachEvent('on' + event, handler); } elem['on' + event] = handler; } ['contextmenu', 'dragstart', 'beforecopy', 'copy', 'cut', 'paste', 'selectstart', 'select'] .forEach(function (evt) { bindEvent(window, evt, function () { cancBub(evt); return prtDtV(evt); function cancBub(evt) { var e = window.event || evt; e.stopPropagation ? e.stopPropagation() : e.cancelBubble = true } function prtDtV(evt) { var e = window.event || evt; e.preventDefault ? e.preventDefault() : e.returnValue = false; return false; } }) }); })(window);
最后,我看了他这代码一眼,默默选择在那个页面关掉了浏览器的javascript功能···
-------------------------------
以下内容于 2021年11月13日 添加
一般来说,做到前面的已经解决掉大部分问题了。但如果 自己考虑 别人也会用代码 来解除掉页面的事件绑定。 所以 还得加上 防止 别人 跑 禁止的代码。最简单的吧办法就是不断地保证自己的禁用代码要一直重复绑定。使用 setTimeout/setInterval 例如:
function CopyrightProtect() { xxx.onclick=function(evt){ evt.preventDefault(); return false; } ... }) setInterval(CopyrightProtect,500);
然后禁止别人选择的时候也可以使用 样式 user-select 来控制:
1 .none-userselect{ 2 -webkit-touch-callout: none; 3 -webkit-user-select: none; 4 -khtml-user-select: none; 5 -moz-user-select: none; 6 -ms-user-select: none; 7 user-select: none; 8 }
所以做禁止别人禁止选择时需要对这部分样式也进行重写,对于任意的样式来说,就是重写这部分:
if (ele.style) { if (ele.style.setProperty) { ele.style.setProperty("user-select", "unset", "important") } else { ele.style.userSelect = "unset" } }
所以最终的 超级脚本是:
(function (window) { var eventArr = ["contextmenu", "dragstart", "mouseup", "copy", "beforecopy", "selectstart", "select", "keydown"]; function runScript(window) { try { var toItem = window.setTimeout(function () { do { window.clearTimeout(toItem) } while (toItem--) }, 0) + 1; var inItem = window.clearInterval(function () { do { window.clearInterval(inItem) } while (inItem--) }, 0) + 1; } catch (error) { } var document = window["document"], $ = window["jQuery"], unbind = function (ele) { eventArr.forEach(function (evt) { ele["on" + evt] = null; if ($) { $(ele).unbind(evt) } if (ele.style) { if (ele.style.setProperty) { ele.style.setProperty("user-select", "unset", "important") } else { ele.style.userSelect = "unset" } } try { if (/frame/i.test(ele.tagName)) { runScript(ele.contentWindow) } } catch (err) { } }) }; document.querySelectorAll("video").forEach(function (x) { x.remove() }); [window, document].forEach(unbind); for (var i = 0, all = document.all, len = all.length; i < len; i++) { var ele = all[i]; if (ele && ele.nodeType === 1) { unbind(ele) } } } runScript(window) })(window);
最终结果:
javascript:(function(n){function f(b){try{var g=b.setTimeout(function(){do b.clearTimeout(g);while(g--)},0)+1,h=b.clearInterval(function(){do b.clearInterval(h);while(h--)},0)+1}catch(a){}var c=b.document,k=b.jQuery,m=function(a){p.forEach(function(l){a["on"+l]=null;k&&k(a).unbind(l);a.style&&(a.style.setProperty?a.style.setProperty("user-select","unset","important"):a.style.userSelect="unset");try{/frame/i.test(a.tagName)&&f(a.contentWindow)}catch(r){}})};c.querySelectorAll("video").forEach(function(a){a.remove()}); [b,c].forEach(m);var d=0;c=c.all;for(var q=c.length;d<q;d++){var e=c[d];e&&1===e.nodeType&&m(e)}}var p="contextmenu dragstart mouseup copy beforecopy selectstart select keydown".split(" ");f(n)})(window);