Greasy Fork

Greasy Fork is available in English.

解除禁止复制粘贴和右键菜单

解除网页的禁止选择、复制、粘贴和右键菜单。注意:这个脚本可能严重影响某些网站功能!

当前为 2025-05-17 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         解除禁止复制粘贴和右键菜单
// @namespace    https://github.com/LaLa-HaHa-Hei/
// @version      1.0.0
// @description  解除网页的禁止选择、复制、粘贴和右键菜单。注意:这个脚本可能严重影响某些网站功能!
// @author       代码见三
// @license      GPL-3.0-or-later
// @match        *://*/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// ==/UserScript==

(function() {
    'use strict';
 
    console.log("运行了Disable Restriction")
    const checkAllElementsForOnEvents = false // 解除限制时检查全部元素,值为true且网页复杂时将导致严重性能问题
    let autoDisableAllRestriction = GM_getValue("dr-autoDisableAllRestriction", false) // 启动页面后自动解除所有限制
    let injectIframesWrittenByJs = GM_getValue("dr-injectIframesWrittenByJs", false) // 部分iframe是由js写入的,不是通过src获取的

    let id1 = GM_registerMenuCommand(
        "自动解除限制:" + (autoDisableAllRestriction === true ? "已开" : "未开"),
        menu1Click,
    "a");
    function menu1Click() {
        GM_unregisterMenuCommand(id1)
        autoDisableAllRestriction = !autoDisableAllRestriction
        GM_setValue("dr-autoDisableAllRestriction", autoDisableAllRestriction)
        
        id1 = GM_registerMenuCommand(
            "自动解除限制:" + (autoDisableAllRestriction === true ? "已开" : "未开"),
            menu1Click,
        "a");
    }
    
    let id2 = GM_registerMenuCommand(
        "包括js写入的iframe:" + (injectIframesWrittenByJs === true ? "已开" : "未开"),
        menu2Click,
    "i");
    function menu2Click() {
        GM_unregisterMenuCommand(id2)
        injectIframesWrittenByJs = !injectIframesWrittenByJs
        GM_setValue("dr-injectIframesWrittenByJs", injectIframesWrittenByJs)
        
        id2 = GM_registerMenuCommand(
            "包括js写入的iframe:" + (injectIframesWrittenByJs === true ? "已开" : "未开"),
            menu2Click,
        "i");
    }

    drMain(autoDisableAllRestriction, checkAllElementsForOnEvents)

    if (injectIframesWrittenByJs)
    {
        injectToAllIframes()
        const injectToAllIframesInterval = setInterval(injectToAllIframes, 2 * 1000)
    }

    // 注入所有js写入的iframe,带有src的ifrmae用match *://*/*匹配
    function injectToAllIframes() {
        const iframes = document.querySelectorAll('iframe');
        iframes.forEach(iframe => {
            try {
                const doc = iframe.contentDocument || iframe.contentWindow.document;
                if (!doc) 
                    return;

                // 避免重复注入
                if (doc.body && doc.body.getAttribute("dr-injected"))
                    return;

                // 注入 drMain()
                const script = doc.createElement('script');
                script.type = 'text/javascript';
                script.textContent = `(${drMain.toString()})(${autoDisableAllRestriction}, ${checkAllElementsForOnEvents});`
                doc.head.appendChild(script);
                doc.body.setAttribute("dr-injected", "true");
                // console.log("已注入 iframe:", iframe);
                console.log("已注入 iframe");
            } catch (e) {
                console.warn("无法注入 iframe(可能是跨域):", e);
            }
        })
    }

    function drMain(autoDisableAllRestriction, checkAllElementsForOnEvents = false){
        if (document.body && document.body.getAttribute("dr-injected-main")) {
            console.log("drMain已经在此frame运行过");
            return;
        }
        if (document.body) {
            document.body.setAttribute("dr-injected-main", "true");
        }
        
        injectCSS()
        injectHTML()

        if (autoDisableAllRestriction)
        {
            disableSelectionRestriction(checkAllElementsForOnEvents)
            disableCopyRestriction(checkAllElementsForOnEvents)
            disablePasteRestriction(checkAllElementsForOnEvents)
            disableContextMenuRestriction(checkAllElementsForOnEvents)
        }

        // 直接绑定会导致找不到元素
        setTimeout(() => {
            bindEvents()
        }, 0);
        
        // 防止被覆盖,重新注入,每2秒检测一次,
        const preventCoveredInterval = setInterval(() => {
            if (!document.querySelector('#dr-html')) {
                console.log('检测到UI被移除,重新注入...');
                injectHTML();
                setTimeout(() => {
                    bindEvents()
                }, 0);
                if (autoDisableAllRestriction)
                {
                    disableSelectionRestriction(checkAllElementsForOnEvents)
                    disableCopyRestriction(checkAllElementsForOnEvents)
                    disablePasteRestriction(checkAllElementsForOnEvents)
                    disableContextMenuRestriction(checkAllElementsForOnEvents)
                }
            }
            if (!document.querySelector('style[dr-style]')) {
                console.log('选择样式被移除,重新注入...');
                injectCSS();
            }
        }, 2 * 1000)
        setTimeout(() => clearInterval(preventCoveredInterval), 7 * 1000); // 监听7秒后不再防止覆盖
        
        // 解除禁止选择,对于实现了拖动功能的元素可能造成影响
        function disableSelectionRestriction(checkAllElementsForOnEvents) {
            // 设置全局css可选则
            const styleId = 'enable-selection-style';
            if (document.getElementById(styleId)) {
                console.log('允许选择css已存在.');
            }
            else {
                const style = document.createElement('style');
                style.id = styleId;
                // 使用 !important 提高优先级,覆盖网站可能使用的 !important
                style.innerHTML = `
                * {
                    user-select: auto !important;
                    -webkit-user-select: auto !important;
                    -moz-user-select: auto !important;
                    -ms-user-select: auto !important;
                }
            `;
                (document.head || document.documentElement).appendChild(style);
                console.log('允许选择css注入完成')
            }

            // 阻止其他addEventListener事件执行
            document.addEventListener('selectstart', e => e.stopImmediatePropagation(), true)
            // onxxx事件和addEventListener本质不同,需要单独阻止
            const elements = checkAllElementsForOnEvents ? document.querySelectorAll('*') : [document, document.body]
            elements.forEach(element => {
                if (typeof element.onselectstart === 'function') {
                    element.onselectstart = null
                }
            })
            console.log('允许选择js注入完成')
        }
        
        // 解除禁止复制
        function disableCopyRestriction(checkAllElementsForOnEvents) {
            document.addEventListener('copy', e => e.stopImmediatePropagation(), true)
            const elements = checkAllElementsForOnEvents ? document.querySelectorAll('*') : [document, document.body]
            elements.forEach(element => {
                if (typeof element.oncopy === 'function') {
                    element.oncopy = null
                }
            })
            console.log('允许复制js注入完成')
        }

        // 解除禁止粘贴
        function disablePasteRestriction(checkAllElementsForOnEvents) {
            document.addEventListener('paste', e => e.stopImmediatePropagation(), true)
            const elements = checkAllElementsForOnEvents ? document.querySelectorAll('*') : document.querySelectorAll('textarea, input, [contenteditable="true"]')
            elements.forEach(element => {
                if (typeof element.onpaste === 'function') {
                    element.onpaste = null
                }
            })
            console.log('允许粘贴js注入完成')
        }
        
        // 解除禁止右键菜单,对于带有特殊菜单的网页可能造成影响
        function disableContextMenuRestriction(checkAllElementsForOnEvents) {
            document.addEventListener('contextmenu', e => e.stopImmediatePropagation(), true)
            // 尝试移除 body/document 上的 oncontextmenu
            const elements = checkAllElementsForOnEvents ? document.querySelectorAll('*') : [document, document.body]
            elements.forEach(element => {
                if (typeof element.oncontextmenu === 'function') {
                    element.oncontextmenu = null
                }
            });
            console.log('允许右键菜单js注入完成');
        }

        function bindEvents(){
            const btn = document.querySelector('#dr-floating-btn');
            const menu = document.querySelector('#dr-menu');
            document.querySelector('#dr-allow-all-btn')?.addEventListener('click', () => {
                disableSelectionRestriction(checkAllElementsForOnEvents)
                disableCopyRestriction(checkAllElementsForOnEvents)
                disablePasteRestriction(checkAllElementsForOnEvents)
                disableContextMenuRestriction(checkAllElementsForOnEvents)
                menu.style.display = 'none'
            });
            document.querySelector('#dr-allow-select-copy-paste-btn')?.addEventListener('click', () => {
                disableSelectionRestriction(checkAllElementsForOnEvents)
                disableCopyRestriction(checkAllElementsForOnEvents)
                disablePasteRestriction(checkAllElementsForOnEvents)
                menu.style.display = 'none'
            });
            document.querySelector('#dr-allow-select-btn')?.addEventListener('click', () => {
                disableSelectionRestriction(checkAllElementsForOnEvents)
                menu.style.display = 'none'
            });
            document.querySelector('#dr-allow-copy-btn')?.addEventListener('click', () => {
                disableCopyRestriction(checkAllElementsForOnEvents)
                menu.style.display = 'none'
            });
            document.querySelector('#dr-allow-paste-btn')?.addEventListener('click', () => {
                disablePasteRestriction(checkAllElementsForOnEvents)
                menu.style.display = 'none'
            });
            document.querySelector('#dr-allow-context-menu-btn')?.addEventListener('click', () => {
                disableContextMenuRestriction(checkAllElementsForOnEvents)
                menu.style.display = 'none'
            });

            // 按钮拖动,点击按钮弹出窗口
            let isDragging = false;
            let isClick = false;
            let offsetY = 0;

            // 点击菜单外关闭菜单
            // 电脑
            document.addEventListener('mousedown', function (e) {
                if (!btn.contains(e.target) && !menu.contains(e.target)) {
                    menu.style.display = 'none';
                }
            });
            // 手机
            document.addEventListener('touchstart', function (e) {
                // 对于触摸事件,e.target 也是触发事件的元素
                if (!btn.contains(e.target) && !menu.contains(e.target)) {
                    menu.style.display = 'none';
                }
            }, { passive: true });

            // 电脑端拖动
            btn.addEventListener('mousedown', handleDragStart);
            document.addEventListener('mousemove', handleDragMove);
            document.addEventListener('mouseup', handleDragEnd);
            // 手机端拖动
            btn.addEventListener("touchstart", handleDragStart);
            document.addEventListener("touchmove", handleDragMove);
            document.addEventListener("touchend", handleDragEnd);

            function handleDragStart(event){
                isDragging = true;
                isClick = true;
                offsetY = (event.clientY || event.touches[0].clientY) - btn.getBoundingClientRect().top;
                menu.style.display = 'none';
                event.preventDefault();
            }
            function handleDragMove(event){
                if (isDragging) {
                    isClick = false;
                    let newTop = (event.clientY || event.touches[0].clientY) - offsetY;
                    // 限制按钮不超出窗口
                    newTop = Math.max(0, Math.min(window.innerHeight - btn.offsetHeight, newTop));
                    btn.style.top = newTop + 'px';
                    event.preventDefault();
                }
            }
            function handleDragEnd(event){
                isDragging = false;
                if (isClick){
                    // 菜单位置跟随按钮
                    const rect = btn.getBoundingClientRect();
                    menu.style.top = rect.top + 'px';
                    menu.style.display = menu.style.display = 'block';
                }
                isClick = false;
            }
        }
            
        // 注入按钮和菜单
        function injectHTML() {
            const injectedTHML = `
                <button id="dr-floating-btn">解除限制</button>
                <div id="dr-menu">
                    <button class="dr-menu-item" id="dr-allow-all-btn">解除所有限制</button>
                    <button class="dr-menu-item" id="dr-allow-select-copy-paste-btn">允许选择复制粘贴</button>
                    <button class="dr-menu-item" id="dr-allow-select-btn">允许选择</button>
                    <button class="dr-menu-item" id="dr-allow-copy-btn">允许复制</button>
                    <button class="dr-menu-item" id="dr-allow-paste-btn">允许粘贴</button>
                    <button class="dr-menu-item" id="dr-allow-context-menu-btn">允许右键菜单</button>
                </div>
            `
            const divElement = document.createElement('div')
            divElement.innerHTML = injectedTHML
            divElement.id = "dr-html"
            document.body.appendChild(divElement)
            // document.body.insertAdjacentHTML('afterbegin', injectedTHML)
        }
        // 注入css
        function injectCSS(){
            const injectedCSS = `
                #dr-floating-btn {
                    position: fixed;
                    left: 0;
                    top: 40%;
                    z-index: 9999;
                    background: #007bff;
                    color: #fff;
                    border: none;
                    border-radius: 0 20px 20px 0;
                    padding: 6px 14px;
                    cursor: pointer;
                    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
                    user-select: none;
                    transition: background 0.2s;
                }

                #dr-floating-btn:active {
                    background: #0056b3;
                }

                #dr-menu {
                    display: none;
                    position: fixed;
                    left: 60px;
                    top: 40%;
                    background: #fff;
                    border-radius: 8px;
                    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
                    padding: 8px 0;
                    z-index: 10000;
                    min-width: 160px;
                }

                .dr-menu-item {
                    display: block;
                    width: 100%;
                    background: none;
                    border: none;
                    text-align: left;
                    padding: 8px 16px;
                    font-size: 15px;
                    color: #333;
                    cursor: pointer;
                    transition: background 0.2s;
                }

                .dr-menu-item:hover {
                    background: #f0f0f0;
                }
            `
            const styleElement = document.createElement('style')
            styleElement.textContent = injectedCSS
            styleElement.setAttribute('dr-style', '')
            document.head.appendChild(styleElement)
        }
    }
})();