Unlock Text Selection & Copy

Universal unlock for text selection, copy, right-click menu, and keyboard shortcuts. Defeats CSS user-select, event interception, key blocking, and clipboard clearing.

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Advertisement:

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

Advertisement:

// ==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('解除选中/复制限制脚本已加载 ✓');
})();