// ==UserScript== // @name 标记已点击链接(会话版·极简) // @description 点击链接变暗并加波浪线,标记持续到标签页关闭 // @version 3.0 // @author WJ // @match *://*/* // @license MIT // @grant none // @namespace https://greasyfork.org/users/914996 // @downloadURL none // ==/UserScript== (() => { 'use strict'; /* ---------- 0. 样式:一条 CSS 搞定 ---------- */ const sheet = new CSSStyleSheet(); sheet.replaceSync('a.x-marked{opacity:.5!important;text-decoration:underline wavy #0ce!important}'); document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet]; /* ---------- 1. 数据:用 Set,省 JSON 往返 ---------- */ const marked = new Set(JSON.parse(sessionStorage.xMarked || '[]')); /* ---------- 2. 标记/恢复 ---------- */ const mark = a => a.classList.toggle('x-marked', marked.has(a.href)); document.querySelectorAll('a[href]').forEach(mark); /* ---------- 3. 动态节点 ---------- */ new MutationObserver(muts => muts.forEach(m => m.addedNodes.forEach(n => n.nodeType === 1 && (n.tagName === 'A' ? mark(n) : n.querySelectorAll?.('a[href]').forEach(mark)) ))).observe(document, {childList: true, subtree: true}); /* ---------- 4. 点击事件:CSS 过滤导航区 ---------- */ document.addEventListener('click', e => { const a = e.target.closest('a[href]:not(nav a, .nav a, .navbar a, .navigation a, .menu a, .menubar a, .breadcrumb a, header a, .header a, footer a, .footer a, .pagination a, .tabs a, .tabbar a, .sidebar a, .aside a, #sidebar a, #aside a, [role="navigation"] a, [role="menu"] a, [role="tablist"] a, [role="banner"] a)'); if (!a) return; marked.add(a.href); sessionStorage.xMarked = JSON.stringify([...marked]); a.classList.add('x-marked'); }, true); })();