您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
点击链接后将其标记为绿色,已点过的链接会持久保存并在页面上自动标绿
// ==UserScript== // @name 点击后链接标绿(可持久保存) // @namespace https://example.com // @version 1.0.0.2025-08-25 // @description 点击链接后将其标记为绿色,已点过的链接会持久保存并在页面上自动标绿 // @author GPT // @match *://*/* // @run-at document-idle // @grant GM_addStyle // @license MIT // ==/UserScript== (function () { 'use strict'; // === 可调整选项 === const OPTIONS = { color: '#0aae4f', // 标记颜色 ignoreHash: true, // 认为 https://a.com/page#x 与 #y 是同一链接 ignoreSearch: false, // 认为 https://a.com/page?a=1 与 ?a=2 不同(若想忽略参数,设为 true) sameOriginOnly: false, // 只标记与当前站点同源的链接 }; const STORAGE_KEY = 'tm_clicked_links_v1'; let clicked = loadSet(); // 注入样式(用 class 控制,优先级高) GM_addStyle(` a.tm-clicked-link { color: ${OPTIONS.color} !important; } `); // 页面初始渲染时,给已记录的链接加绿 markAllOnPage(); // 监听点击(左键、带 Ctrl/Meta 的新标签打开等) window.addEventListener('click', handleAnyClick, true); // 监听中键点击/其他辅助点击 window.addEventListener('auxclick', handleAnyClick, true); // 有些站点会动态加载内容,使用简单的观察器给新节点补标记 const mo = new MutationObserver(debounced(markAllOnPage, 200)); mo.observe(document.documentElement, { childList: true, subtree: true }); // ====== 具体函数 ====== function handleAnyClick(e) { const a = e.target && closestAnchor(e.target); if (!a) return; if (!a.href) return; if (OPTIONS.sameOriginOnly && new URL(a.href, location.href).origin !== location.origin) return; const key = linkKey(a.href); // 记录并立即标色 if (!clicked.has(key)) { clicked.add(key); saveSet(clicked); } markAnchor(a); } function markAllOnPage() { const anchors = document.querySelectorAll('a[href]'); for (const a of anchors) { if (!a.href) continue; if (OPTIONS.sameOriginOnly && new URL(a.href, location.href).origin !== location.origin) continue; if (clicked.has(linkKey(a.href))) markAnchor(a); } } function markAnchor(a) { a.classList.add('tm-clicked-link'); } function closestAnchor(el) { return el.closest ? el.closest('a[href]') : null; } function linkKey(href) { const u = new URL(href, location.href); if (OPTIONS.ignoreHash) u.hash = ''; if (OPTIONS.ignoreSearch) u.search = ''; // 统一移除结尾斜杠的差异(/path 与 /path/ 视为同一) u.pathname = u.pathname.replace(/\/+$/, ''); // 小写协议与主机,保留路径大小写 return `${u.protocol.toLowerCase()}//${u.host.toLowerCase()}${u.pathname}${u.search}${u.hash}`; } function loadSet() { try { const raw = localStorage.getItem(STORAGE_KEY); return new Set(raw ? JSON.parse(raw) : []); } catch { return new Set(); } } function saveSet(set) { try { localStorage.setItem(STORAGE_KEY, JSON.stringify([...set])); } catch { // localStorage 可能满了,忽略 } } // 简单防抖 function debounced(fn, delay) { let t; return (...args) => { clearTimeout(t); t = setTimeout(() => fn.apply(null, args), delay); }; } })();