您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
解除各站 IMG 的 oncontextmenu/onmousedown/onselectstart/ondragstart 等攔截,恢復右鍵、選取、拖曳;支援 per-domain 開關與 file://。
当前为
// ==UserScript== // @name 解鎖圖片右鍵與選取(全站版,可 per-domain 關閉) // @namespace http://greasyfork.icu/users/your-id // @version 1.1.0 // @description 解除各站 IMG 的 oncontextmenu/onmousedown/onselectstart/ondragstart 等攔截,恢復右鍵、選取、拖曳;支援 per-domain 開關與 file://。 // @author you // @match *://*/* // @match file:///* // @run-at document-start // @license MIT // @grant GM_registerMenuCommand // @grant GM_getValue // @grant GM_setValue // @grant GM_addStyle // ==/UserScript== (function () { 'use strict'; const KEY = 'unlock_img_rightclick.disabled_domains'; const ATTRS = ['oncontextmenu','onmousedown','onselectstart','ondragstart']; const domain = location.hostname || 'file'; const getSet = async () => new Set(JSON.parse(await GM_getValue(KEY, '[]'))); const saveSet = async (set) => GM_setValue(KEY, JSON.stringify([...set])); // Menu: toggle current domain (async () => { const disabled = await getSet(); const isDisabled = disabled.has(domain); GM_registerMenuCommand( `${isDisabled ? '啟用' : '停用'} 此網域:${domain}`, async () => { if (isDisabled) disabled.delete(domain); else disabled.add(domain); await saveSet(disabled); location.reload(); } ); GM_registerMenuCommand('查看已停用網域', async () => { const list = [...await getSet()].sort().join('\n') || '(無)'; alert(list); }); GM_registerMenuCommand('清空停用清單', async () => { if (confirm('確定清空所有停用網域?')) await saveSet(new Set()); location.reload(); }); if (isDisabled) return; // do nothing on disabled domains init(); })(); function init() { // CSS:恢復拖曳/選取與指針事件 GM_addStyle(` img { -webkit-user-drag: auto !important; user-select: auto !important; } img, picture, figure { pointer-events: auto !important; } `); // 捕獲階段阻擋站方攔截器(不阻止預設行為) ['contextmenu','mousedown','selectstart','dragstart'].forEach(type => { document.addEventListener(type, ev => { if (ev.target instanceof HTMLImageElement) { ev.stopImmediatePropagation(); // 擋站方處理器 // 不呼叫 preventDefault(),保留瀏覽器右鍵 } }, true); }); const cleanNode = (node) => { if (!(node instanceof Element)) return; if (node instanceof HTMLImageElement || ATTRS.some(a => node.hasAttribute(a))) { for (const a of ATTRS) { if (node.hasAttribute(a)) node.removeAttribute(a); try { node[a] = null; } catch {} } if (node instanceof HTMLImageElement) { node.style.userSelect = 'auto'; node.style.webkitUserDrag = 'auto'; } } }; const scan = (root) => { cleanNode(root); root.querySelectorAll?.('img,[oncontextmenu],[onmousedown],[onselectstart],[ondragstart]') .forEach(cleanNode); }; const mo = new MutationObserver(muts => { for (const m of muts) { if (m.type === 'childList') { m.addedNodes.forEach(scan); } else if (m.type === 'attributes') { cleanNode(m.target); } } }); mo.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ATTRS }); if (document.readyState !== 'loading') scan(document); else document.addEventListener('DOMContentLoaded', () => scan(document)); } })();