- Added mousedown listener for middle-click (button 1) events - Added contextmenu listener for right-click menu actions - Ensures posts opened in new tabs are marked as visited
453 lines
14 KiB
JavaScript
453 lines
14 KiB
JavaScript
// ==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');
|
||
}
|
||
}
|
||
});
|
||
|
||
// 处理鼠标中键点击(新标签页打开)
|
||
link.addEventListener('mousedown', function(e) {
|
||
// 中键点击 (button === 1)
|
||
if (e.button === 1) {
|
||
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');
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
// 处理右键菜单"在新标签页中打开"
|
||
link.addEventListener('contextmenu', function() {
|
||
// 延迟执行,让用户有机会选择菜单项
|
||
setTimeout(() => {
|
||
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');
|
||
}
|
||
}
|
||
}, 100);
|
||
});
|
||
});
|
||
}
|
||
|
||
// 处理热搜帖子的显示/隐藏
|
||
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 = `
|
||
<h4>51吃瓜助手</h4>
|
||
<div class="switch-container">
|
||
<label class="switch">
|
||
<input type="checkbox" id="hideHotSwitch">
|
||
<span class="slider"></span>
|
||
</label>
|
||
<span class="switch-label">隐藏热搜</span>
|
||
</div>
|
||
<div class="switch-container">
|
||
<label class="switch">
|
||
<input type="checkbox" id="markVisitedSwitch" checked>
|
||
<span class="slider"></span>
|
||
</label>
|
||
<span class="switch-label">标记已读</span>
|
||
</div>
|
||
`;
|
||
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();
|
||
}
|
||
})(); |