网页枷锁破除

解除网页右键/选择/复制及拖拽限制 恢复自由交互体验

当前为 2025-04-15 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Universal Web Liberator
  3. // @name:zh-CN 网页枷锁破除
  4. // @name:zh-TW 網頁枷鎖破除
  5. // @description Regain Control Unlocks RightClick/Selection/CopyPaste/Drag On Any Website
  6. // @description:zh-CN 解除网页右键/选择/复制及拖拽限制 恢复自由交互体验
  7. // @description:zh-TW 解除網頁右鍵/選取/複製及拖曳限制 恢復自由互動體驗
  8. // @version 1.1.2
  9. // @icon https://raw.githubusercontent.com/MiPoNianYou/UserScripts/refs/heads/main/Icons/UniversalWebLiberatorIcon.svg
  10. // @author 念柚
  11. // @namespace https://github.com/MiPoNianYou/UserScripts
  12. // @supportURL https://github.com/MiPoNianYou/UserScripts/issues
  13. // @license GPL-3.0
  14. // @match *://*/*
  15. // @grant none
  16. // @run-at document-start
  17. // ==/UserScript==
  18.  
  19. class WebLiberator {
  20. static EventsToStop = ["contextmenu", "selectstart", "copy", "cut", "paste"];
  21.  
  22. static RestrictedEventProps = [
  23. "oncontextmenu",
  24. "onselectstart",
  25. "oncopy",
  26. "oncut",
  27. "onpaste",
  28. "ondrag",
  29. "ondragstart",
  30. ];
  31.  
  32. constructor() {
  33. this.InitMutationObserver();
  34. this.ExecuteCoreFeatures();
  35. }
  36.  
  37. ExecuteCoreFeatures() {
  38. this.PurgeEventListeners(document.documentElement);
  39. this.InjectLiberationStyles();
  40. this.BindGlobalEvents();
  41. this.ProcessExistingNodes();
  42. }
  43.  
  44. StopImmediatePropagationHandler(event) {
  45. event.stopImmediatePropagation();
  46. }
  47.  
  48. BindGlobalEvents() {
  49. WebLiberator.EventsToStop.forEach((type) => {
  50. document.addEventListener(
  51. type,
  52. this.StopImmediatePropagationHandler,
  53. true
  54. );
  55. });
  56. }
  57.  
  58. InjectLiberationStyles() {
  59. const StyleSheet = new CSSStyleSheet();
  60. StyleSheet.replaceSync(`
  61. *, *::before, *::after {
  62. user-select: text !important;
  63. -webkit-user-drag: auto !important;
  64. user-drag: auto !important;
  65. }
  66. `);
  67. document.adoptedStyleSheets = [...document.adoptedStyleSheets, StyleSheet];
  68. }
  69.  
  70. ProcessExistingNodes() {
  71. requestIdleCallback(() => {
  72. if (document.body) {
  73. this.PurgeEventListeners(document.body);
  74. const elements = document.body.querySelectorAll("*");
  75. for (const element of elements) {
  76. this.PurgeEventListeners(element);
  77. }
  78. }
  79. });
  80. }
  81.  
  82. PurgeEventListeners(element) {
  83. if (!element || element.nodeType !== Node.ELEMENT_NODE) return;
  84.  
  85. for (const prop of WebLiberator.RestrictedEventProps) {
  86. element[prop] = null;
  87. }
  88. }
  89.  
  90. HandleMutation(mutations) {
  91. for (const mutation of mutations) {
  92. if (mutation.type === "childList") {
  93. for (const node of mutation.addedNodes) {
  94. if (node.nodeType === Node.ELEMENT_NODE) {
  95. this.PurgeEventListeners(node);
  96. const descendants = node.querySelectorAll("*");
  97. for (const child of descendants) {
  98. this.PurgeEventListeners(child);
  99. }
  100. }
  101. }
  102. }
  103. }
  104. }
  105.  
  106. InitMutationObserver() {
  107. this.observer = new MutationObserver(this.HandleMutation.bind(this));
  108. this.observer.observe(document.documentElement, {
  109. childList: true,
  110. subtree: true,
  111. attributes: false,
  112. });
  113. }
  114. }
  115.  
  116. new WebLiberator();