// ==UserScript== // @name 51吃瓜网助手 // @namespace http://tampermonkey.net/ // @version 1.0.0 // @description 隐藏热搜HOT帖子,标记已读帖子 // @author Your Name // @match https://51cg1.com/* // @match https://www.51cg1.com/* // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @run-at document-end // ==/UserScript== (function() { 'use strict'; // 添加样式 GM_addStyle(` /* 控制按钮样式 */ .cg-helper-controls { position: fixed; top: 100px; right: 20px; z-index: 9999; background: white; border-radius: 8px; padding: 15px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); min-width: 150px; } .cg-helper-controls h4 { margin: 0 0 10px 0; font-size: 14px; color: #333; } /* 开关样式 */ .switch-container { display: flex; align-items: center; margin-bottom: 10px; } .switch { position: relative; display: inline-block; width: 50px; height: 24px; margin-right: 10px; } .switch input { opacity: 0; width: 0; height: 0; } .slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: .4s; border-radius: 24px; } .slider:before { position: absolute; content: ""; height: 18px; width: 18px; left: 3px; bottom: 3px; background-color: white; transition: .4s; border-radius: 50%; } input:checked + .slider { background-color: #2196F3; } input:checked + .slider:before { transform: translateX(26px); } .switch-label { font-size: 13px; color: #666; } /* 隐藏热搜帖子和广告帖子的样式类 */ .hot-post-hidden, .ad-post-hidden { display: none !important; margin: 0 !important; padding: 0 !important; height: 0 !important; overflow: hidden !important; visibility: hidden !important; position: absolute !important; width: 0 !important; } /* 确保隐藏article标签时不留空白,但排除内容页的article.post */ article:not(.post).hot-post-hidden, article:not(.post).ad-post-hidden { display: none !important; } /* 修复第一个可见帖子的上边距,但不影响内容页 */ #index article:not(.hot-post-hidden):not(.ad-post-hidden):not(.post) { margin-top: 0; } /* 如果article被隐藏,确保下一个不会有额外间距 */ #index article.hot-post-hidden + article, #index article.ad-post-hidden + article { margin-top: 0 !important; } /* 已读帖子样式 - 标题删除线 + 图片黑白 */ .post-card.visited .post-card-title { text-decoration: line-through; opacity: 0.6; } /* 已读帖子的背景图片变黑白 */ .post-card.visited .blog-background { filter: grayscale(100%); opacity: 0.7; } /* 已读帖子整体透明度降低 */ .post-card.visited { opacity: 0.65; } /* 深色模式适配 */ body.theme-dark .cg-helper-controls { background: #2b2b2b; color: #e0e0e0; } body.theme-dark .cg-helper-controls h4 { color: #e0e0e0; } body.theme-dark .switch-label { color: #b0b0b0; } `); // 获取已访问的帖子列表 function getVisitedPosts() { const visited = GM_getValue('visitedPosts', '[]'); try { return JSON.parse(visited); } catch (e) { return []; } } // 保存已访问的帖子 function saveVisitedPosts(posts) { GM_setValue('visitedPosts', JSON.stringify(posts)); } // 标记帖子为已访问 function markPostAsVisited(postId) { let visited = getVisitedPosts(); // 如果已存在,先移除旧的位置 const existingIndex = visited.indexOf(postId); if (existingIndex !== -1) { visited.splice(existingIndex, 1); } // 添加到末尾(最新的) visited.push(postId); // 只保留最近的1000个记录,避免数据过大 // 使用 while 循环确保数组长度不超过 1000 while (visited.length > 1000) { visited.shift(); // 移除最旧的记录 } saveVisitedPosts(visited); // 输出调试信息 console.log(`已标记帖子 ${postId} 为已读,当前已读记录数:${visited.length}`); } // 应用已访问样式 function applyVisitedStyles() { const visited = getVisitedPosts(); visited.forEach(postId => { const postCard = document.getElementById(`post-card-${postId}`); if (postCard) { postCard.classList.add('visited'); } }); } // 监听点击事件 function setupClickListeners() { // 监听所有帖子链接的点击 document.querySelectorAll('a[href*="/archives/"]').forEach(link => { link.addEventListener('click', function() { const match = this.href.match(/\/archives\/(\d+)\//); if (match) { const postId = match[1]; markPostAsVisited(postId); // 立即应用样式 const postCard = document.getElementById(`post-card-${postId}`); if (postCard) { postCard.classList.add('visited'); } } }); }); } // 处理热搜帖子的显示/隐藏 function toggleHotPosts(hide) { // 判断是否在文章内容页面 const isArticlePage = window.location.pathname.includes('/archives/') || document.querySelector('article.post') !== null; // 如果是文章内容页面,不执行隐藏操作 if (isArticlePage) { console.log('文章内容页面,不执行隐藏操作'); return; } let hotCount = 0; let adCount = 0; // 1. 查找所有包含"热搜 HOT"标签的帖子(仅在列表页) document.querySelectorAll('.wrap .wraps').forEach(element => { if (element.textContent.includes('热搜 HOT')) { // 优先找到最近的 article 元素,但排除内容页的 article.post let articleElement = element.closest('article'); // 确保不是文章内容页的主article if (articleElement && !articleElement.classList.contains('post')) { if (hide) { // 使用 display: none 和 !important 确保完全隐藏 articleElement.style.cssText = 'display: none !important;'; articleElement.classList.add('hot-post-hidden'); hotCount++; } else { // 恢复显示 articleElement.style.cssText = ''; articleElement.classList.remove('hot-post-hidden'); } } } }); // 2. 同时隐藏广告帖子(作者为AD1234或包含post-card-ads类) // 只处理列表页的 article,不处理 article.post document.querySelectorAll('article:not(.post)').forEach(article => { // 检查是否是广告帖子 const authorMeta = article.querySelector('meta[itemprop="name"]'); const isAd = (authorMeta && authorMeta.content === 'AD1234') || article.querySelector('.post-card-ads') || article.querySelector('a[rel*="sponsored"]'); if (isAd) { if (hide) { article.style.cssText = 'display: none !important;'; article.classList.add('ad-post-hidden'); adCount++; } else { // 只恢复非广告的帖子 if (article.classList.contains('ad-post-hidden')) { article.style.cssText = ''; article.classList.remove('ad-post-hidden'); } } } }); // 调试信息 if (hide) { console.log(`已隐藏 ${hotCount} 个热搜帖子,${adCount} 个广告帖子`); } } // 创建控制面板 function createControlPanel() { const panel = document.createElement('div'); panel.className = 'cg-helper-controls'; panel.innerHTML = `

51吃瓜助手

隐藏热搜
标记已读
`; document.body.appendChild(panel); // 热搜开关 const hideHotSwitch = document.getElementById('hideHotSwitch'); hideHotSwitch.checked = GM_getValue('hideHot', false); if (hideHotSwitch.checked) { toggleHotPosts(true); } hideHotSwitch.addEventListener('change', function() { GM_setValue('hideHot', this.checked); toggleHotPosts(this.checked); }); // 标记已读开关 const markVisitedSwitch = document.getElementById('markVisitedSwitch'); markVisitedSwitch.checked = GM_getValue('markVisited', true); markVisitedSwitch.addEventListener('change', function() { GM_setValue('markVisited', this.checked); if (this.checked) { applyVisitedStyles(); } else { // 移除所有已访问样式 document.querySelectorAll('.post-card.visited').forEach(card => { card.classList.remove('visited'); }); } }); } // 处理动态加载的内容 function observeNewContent() { const observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.addedNodes.length) { // 重新应用已访问样式 if (GM_getValue('markVisited', true)) { applyVisitedStyles(); } // 重新应用热搜隐藏 if (GM_getValue('hideHot', false)) { toggleHotPosts(true); } // 重新设置点击监听 setupClickListeners(); } }); }); // 观察主内容区域的变化 const contentArea = document.querySelector('.body-container-wrapper'); if (contentArea) { observer.observe(contentArea, { childList: true, subtree: true }); } } // 初始化 function init() { // 清理超过限制的已读记录 cleanupVisitedRecords(); // 创建控制面板 createControlPanel(); // 应用保存的设置 if (GM_getValue('hideHot', false)) { toggleHotPosts(true); } if (GM_getValue('markVisited', true)) { applyVisitedStyles(); } // 设置点击监听 setupClickListeners(); // 监听动态内容 observeNewContent(); } // 清理超过限制的已读记录 function cleanupVisitedRecords() { let visited = getVisitedPosts(); if (visited.length > 1000) { // 只保留最新的 1000 条记录 visited = visited.slice(-1000); saveVisitedPosts(visited); console.log(`已清理已读记录,保留最新的 1000 条`); } } // 等待DOM加载完成 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();