// ==UserScript== // @name v2ex屏蔽器 // @namespace http://tampermonkey.net/ // @version 2.7 // @description 支持关键词屏蔽 + 动态更新 + 开关切换不刷新 // @author YourName // @match *://*.v2ex.com/* // @grant GM_getValue // @grant GM_setValue // ==/UserScript== (function() { 'use strict'; // 默认配置 const defaultConfig = { keywords: ['结婚', '彩礼', '婚礼'], blockMode: 'hide', enabled: true, titleSelector: 'div.cell.item', rowSelector: 'span.item_title a' }; // 读取配置 const config = { keywords: GM_getValue('keywords', defaultConfig.keywords), blockMode: GM_getValue('blockMode', defaultConfig.blockMode), enabled: GM_getValue('enabled', defaultConfig.enabled), titleSelector: GM_getValue('titleSelector', defaultConfig.titleSelector), rowSelector: GM_getValue('rowSelector', defaultConfig.rowSelector) }; // 创建悬浮图标 function createTriggerIcon() { const icon = document.createElement('div'); icon.id = 'content-blocker-icon'; icon.title = '点击配置屏蔽规则'; icon.style = ` position: fixed; top: 20px; right: 20px; width: 32px; height: 32px; background: #4CAF50; color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; z-index: 9998; font-size: 18px; box-shadow: 0 2px 6px rgba(0,0,0,0.2); user-select: none; transition: transform 0.2s; `; icon.innerHTML = '⚙️'; document.body.appendChild(icon); // 添加点击事件 icon.addEventListener('click', () => { toggleSettingsPanel(); icon.style.transform = document.getElementById('content-blocker-panel').style.display === 'block' ? 'scale(1.1)' : 'scale(1)'; }); return icon; } // 创建配置面板(默认隐藏) function createSettingsPanel() { const panel = document.createElement('div'); panel.id = 'content-blocker-panel'; panel.style = ` position: fixed; top: 60px; right: 20px; background: white; border: 1px solid #ccc; border-radius: 8px; padding: 15px; z-index: 9997; font-family: Arial, sans-serif; box-shadow: 0 4px 12px rgba(0,0,0,0.15); max-width: 300px; display: none; /* 默认隐藏 */ `; panel.innerHTML = `

内容屏蔽设置

`; document.body.appendChild(panel); // 初始化关键词列表 updateKeywordList(); // 事件绑定 document.getElementById('add-keyword').addEventListener('click', addKeyword); document.getElementById('save-settings').addEventListener('click', saveSettings); document.getElementById('new-keyword').addEventListener('keypress', (e) => { if (e.key === 'Enter') addKeyword(); }); // 启用/禁用开关 document.getElementById('enable-toggle').addEventListener('change', (e) => { config.enabled = e.target.checked; GM_setValue('enabled', config.enabled); initBlocker(); // 不刷新页面,直接更新屏蔽效果 }); return panel; } // 切换面板显示状态 function toggleSettingsPanel() { const panel = document.getElementById('content-blocker-panel'); panel.style.display = panel.style.display === 'block' ? 'none' : 'block'; } // 点击外部区域隐藏面板 document.addEventListener('click', function (e) { const panel = document.getElementById('content-blocker-panel'); const icon = document.getElementById('content-blocker-icon'); // 如果面板不存在或未显示,直接返回 if (!panel || panel.style.display !== 'block') return; // 点击目标不在面板或图标上时隐藏面板 if (!panel.contains(e.target) && !icon?.contains(e.target)) { panel.style.display = 'none'; } }); // 更新关键词列表 function updateKeywordList() { const container = document.getElementById('keyword-list'); container.innerHTML = ''; config.keywords.forEach((keyword, index) => { const div = document.createElement('div'); div.style = "display:flex;justify-content:space-between;margin:2px 0;"; div.innerHTML = ` ${keyword} `; container.appendChild(div); }); // 事件委托:统一处理删除按钮点击 container.addEventListener('click', function (e) { if (e.target.classList.contains('delete-btn')) { e.stopPropagation(); // 阻止事件冒泡 const index = parseInt(e.target.getAttribute('data-index')); removeKeyword(index); } }); } // 删除关键词 function removeKeyword(index) { if (index >= 0 && index < config.keywords.length) { config.keywords.splice(index, 1); GM_setValue('keywords', config.keywords); updateKeywordList(); // 刷新关键词列表 initBlocker(); // 立即应用新的屏蔽规则 } } // 添加关键词 function addKeyword() { const input = document.getElementById('new-keyword'); const keyword = input.value.trim(); if (keyword && !config.keywords.includes(keyword)) { config.keywords.push(keyword); GM_setValue('keywords', config.keywords); updateKeywordList(); input.value = ''; initBlocker(); // 立即应用新的屏蔽规则 } } // 保存设置 function saveSettings() { config.blockMode = document.getElementById('block-mode').value; GM_setValue('blockMode', config.blockMode); location.reload(); // 保存后直接刷新页面 } // 主屏蔽逻辑 function initBlocker() { if (!config.enabled) return; if (!config.keywords.length) return; const lowerCaseKeywords = config.keywords.map(k => k.toLowerCase()); //const pattern = new RegExp(config.keywords.join('|'), 'i'); /** * 获取页面上所有的帖子项 * V2EX 的帖子项通常是
*/ const topicItems = document.querySelectorAll(config.titleSelector); /** * 遍历所有帖子 */ topicItems.forEach(item => { // 获取帖子标题元素 const titleElement = item.querySelector(config.rowSelector); if (titleElement) { const title = titleElement.innerText.toLowerCase(); // 获取标题文本并转为小写 // 检查标题是否包含任何一个需要屏蔽的关键词 const shouldBlock = lowerCaseKeywords.some(keyword => title.includes(keyword)); if (shouldBlock) { // 如果包含,则隐藏整个帖子项 applyBlockStyle(item, config.blockMode); console.log(`已屏蔽: ${titleElement.href}`); } } }); } // 查找最近的匹配父元素 function findClosestElement(element, selector) { let current = element; while (current && current !== document.body) { if (current.matches?.(selector)) return current; current = current.parentNode; } return null; } // 应用屏蔽样式 function applyBlockStyle(element, mode) { switch(mode) { case 'remove': element.remove(); break; case 'blur': element.style.filter = 'blur(5px)'; break; case 'hide': default: element.style.display = 'none'; } } // 初始化 const icon = createTriggerIcon(); const panel = createSettingsPanel(); initBlocker(); // 监听动态内容 const observer = new MutationObserver(initBlocker); observer.observe(document.body, { childList: true, subtree: true }); })();