Universal unlock for text selection, copy, right-click menu, and keyboard shortcuts. Defeats CSS user-select, event interception, key blocking, and clipboard clearing.
// ==UserScript==
// @name Unlock Text Selection & Copy
// @name:zh-CN 解除网页禁止选中/复制限制
// @name:en Unlock Text Selection & Copy
// @namespace https://alicexia.app/
// @version 2.0.0
// @description Universal unlock for text selection, copy, right-click menu, and keyboard shortcuts. Defeats CSS user-select, event interception, key blocking, and clipboard clearing.
// @description:zh-CN 通用解除网页禁止选中文本、复制文本、右键菜单、快捷键等所有限制。支持 CSS user-select、事件拦截、快捷键封锁、剪贴板清空等全部手段。
// @author alicexia
// @match https://*.hotjob.cn/*
// @match https://*.zhiye.com/*
// @match https://*.iguopin.com/*
// @grant none
// @run-at document-start
// @license MIT
// @contributionURL https://github.com/alicexiacn
// @homepageURL https://github.com/alicexiacn/unlock-copy
// @supportURL https://github.com/alicexiacn/unlock-copy/issues
// @icon data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%234CAF50' d='M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z'/%3E%3C/svg%3E
// @compatible chrome
// @compatible edge
// @compatible firefox
// @compatible opera
// @compatible safari
// ==/UserScript==
(function () {
'use strict';
// ============================================================
// 第一层:CSS 防护解除
// 覆盖 user-select: none / pointer-events: none
// ============================================================
function injectCSS() {
var style = document.createElement('style');
style.id = 'wb-unlock-select-style';
style.textContent = [
'*, *::before, *::after {',
' -webkit-user-select: text !important;',
' -moz-user-select: text !important;',
' -ms-user-select: text !important;',
' user-select: text !important;',
' -webkit-touch-callout: default !important;',
' pointer-events: auto !important;',
'}'
].join('\n');
(document.head || document.documentElement).appendChild(style);
}
// ============================================================
// 第二层:事件拦截解除
// 在捕获阶段(capture)用 stopImmediatePropagation 阻断
// 网站自己注册的 contextmenu / selectstart / copy 等监听器
// ============================================================
var blockedEvents = [
'contextmenu', // 右键菜单
'selectstart', // 开始选中
'select', // 选中过程
'copy', // 复制
'cut', // 剪切
'paste', // 粘贴
'dragstart', // 拖拽
'beforecopy', // 复制前
'beforecut', // 剪切前
'beforepaste', // 粘贴前
'mousedown', // 鼠标按下(部分网站用它阻止选区起点)
'mouseup' // 鼠标抬起
];
// 对普通用户来说,mousedown/mouseup 全局拦截可能影响交互
// 这里只阻断那些调用了 preventDefault 的处理
blockedEvents.forEach(function (eventName) {
document.addEventListener(eventName, function (e) {
e.stopPropagation();
// stopImmediatePropagation 会阻止同元素上后续注册的同类监听器执行
// 但只对 selectstart / contextmenu / copy 等关键事件使用
if (['contextmenu', 'selectstart', 'copy', 'cut', 'paste', 'dragstart'].indexOf(eventName) !== -1) {
e.stopImmediatePropagation();
}
// 放行默认行为
try { e.returnValue = true; } catch (_) {}
}, true); // true = 捕获阶段,比网站的监听器更早执行
});
// ============================================================
// 第三层:快捷键解除
// 阻断网站对 Ctrl+C / Ctrl+A / Ctrl+X / Ctrl+V 的拦截
// ============================================================
document.addEventListener('keydown', function (e) {
if (e.ctrlKey || e.metaKey) {
var key = (e.key || '').toLowerCase();
if (['c', 'a', 'x', 'v', 'insert'].indexOf(key) !== -1) {
e.stopPropagation();
e.stopImmediatePropagation();
}
}
// F12 / Ctrl+Shift+I / Ctrl+Shift+J / Ctrl+U 也一并放行
if (e.key === 'F12' || (e.ctrlKey && e.shiftKey && ['i', 'j', 'c'].indexOf((e.key || '').toLowerCase()) !== -1) || (e.ctrlKey && (e.key || '').toLowerCase() === 'u')) {
e.stopPropagation();
e.stopImmediatePropagation();
}
}, true);
// ============================================================
// 第四层:清除内联事件处理器 & 保护剪贴板
// ============================================================
var inlineProps = [
'oncontextmenu', 'onselectstart', 'oncopy', 'oncut',
'onpaste', 'ondragstart', 'onmousedown', 'onmouseup',
'onbeforecopy', 'onbeforecut', 'onbeforepaste'
];
function clearInlineHandlers() {
inlineProps.forEach(function (prop) {
try {
document[prop] = null;
if (document.body) { document.body[prop] = null; }
document.documentElement[prop] = null;
} catch (_) {}
});
}
// 用 Object.defineProperty 彻底锁死,防止网站重新赋值
inlineProps.forEach(function (prop) {
try {
Object.defineProperty(document, prop, {
get: function () { return null; },
set: function () { /* 吞掉赋值 */ },
configurable: true
});
} catch (_) {}
});
// ============================================================
// 第五层:MutationObserver 持续监控
// 部分网站会在 DOM 动态变化后重新注入限制
// ============================================================
var observer = new MutationObserver(function () {
clearInlineHandlers();
});
function startObserver() {
observer.observe(document.documentElement || document, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['oncontextmenu', 'onselectstart', 'oncopy', 'oncut',
'onpaste', 'ondragstart', 'style']
});
}
// ============================================================
// 初始化:多个时机执行,确保覆盖所有加载阶段
// ============================================================
// 1. 立即注入 CSS(document-start 时 head 可能还不存在,先放 html 上)
injectCSS();
// 2. DOMContentLoaded 时清除内联事件 + 重新注入 CSS + 启动 Observer
document.addEventListener('DOMContentLoaded', function () {
clearInlineHandlers();
injectCSS();
startObserver();
});
// 3. window.load 时再补一次(处理懒加载脚本)
window.addEventListener('load', function () {
clearInlineHandlers();
injectCSS();
});
// 4. 如果 document 已经 ready,立即执行
if (document.readyState !== 'loading') {
clearInlineHandlers();
injectCSS();
startObserver();
}
console.log('解除选中/复制限制脚本已加载 ✓');
})();