您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
自动生成响应式目录,支持清除广告、不相关内容,适配夜间模式
// ==UserScript== // @name 🌿 做个绿色网站 // @namespace https://github.com/ricsy/GreenWeb // @version 1.0.2 // @license MIT // @description 自动生成响应式目录,支持清除广告、不相关内容,适配夜间模式 // @author ricsy // @match *://www.jianshu.com/p/* // @match *://blog.csdn.net/*/article/details/* // @icon none // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js // @grant GM_addStyle // ==/UserScript== /* =============================== 全局错误监听 =============================== */ window.addEventListener('error', (e) => { // console.log(`[GreenWeb] [全局异常] ${e.message} @${e.filename}:${e.lineno}`); }); /* =============================== 元素标签 =============================== */ const elementTag = (() => { const CONFIG = { 'www.jianshu.com': { 'article': { name_zh: '文章标签', hide: false, }, 'aside': { name_zh: '侧边栏标签', hide: true, }, }, 'blog.csdn.net': { 'article': { name_zh: '文章标签', hide: false, }, } }; return { // 根据中文名称查询 getTagByChineseName: (name_zh) => { let result = ''; const host = window.location.hostname; const platform = Object.keys(CONFIG).find(k => host.includes(k)); if (!platform) return result; for (const [tag, config] of Object.entries(CONFIG[platform])) { const zhName = typeof config === 'object' ? config.name_zh : config; if (zhName === name_zh) { return { 'tag': tag, 'hide': config?.hide || false }; } } return result; }, } })(); /* =============================== 页面元素 =============================== */ const webElement = (() => { const CONFIG = { 'www.jianshu.com': { '.aside': { name_zh: '右侧广告栏', selector_type: 'class', name_en: 'aside', type: 'official', dynamic: true, hide: true, }, '#side-menu-toc': { name_zh: '目录主容器', selector_type: 'id', name_en: 'side-menu-toc', type: 'custom' }, '#menu_toc_ol': { name_zh: '目录列表', selector_type: 'id', name_en:'menu_toc_ol', type: 'custom' }, '#ad-control': { name_zh: '广告控制栏', selector_type: 'id', name_en:'ad-control', type: 'custom' }, }, 'blog.csdn.net': { '.toolbar-container': { name_zh: '顶部工具栏', selector_type: 'class', name_en: 'toolbar-container', type: 'official', hide: true, }, '#asideProfile': { name_zh: '个人信息栏', selector_type: 'id', name_en: 'asideProfile', type: 'official', hide: true, }, '#asideHotArticle': { name_zh: '热门文章栏', selector_type: 'id', name_en: 'asideHotArticle', type: 'official', hide: true, }, '#asideNewComments': { name_zh: '最新评论栏', selector_type: 'id', name_en: 'asideNewComments', type: 'official', hide: true, }, '#footerRightAds': { name_zh: '底部广告栏', selector_type: 'id', name_en: 'footerRightAds', type: 'official', hide: true, }, '#recommend-right': { name_zh: '推荐栏', selector_type: 'id', name_en: 'recommend-right', type: 'official', hide: true, }, '#recommendAdBox': { name_zh: '推荐广告栏', selector_type: 'id', name_en:'recommendAdBox', type: 'official', hide: true, }, '#rightAside': { name_zh: '右侧广告栏', selector_type: 'id', name_en:'rightAside', type: 'official', hide: true, }, '.groupfile-div': { name_zh: '文件分组栏', selector_type: 'class', name_en:'groupfile-div', type: 'official', hide: true, }, '.aside-title': { name_zh: '侧边栏标题', selector_type: 'class', name_en:'aside-title', type: 'official', hide: true, }, '.aside-content': { name_zh: '侧边栏内容', selector_type: 'class', name_en:'aside-content', type: 'official', hide: true, }, '.btn-code-notes.ckeditor': { name_zh: '代码笔记按钮', selector_type: 'class', name_en:'btn-code-notes.ckeditor', type: 'official', hide: true, }, '.recommend-box.insert-baidu-box': { name_zh: '百度推荐栏', selector_type: 'class', name_en:'recommend-box.insert-baidu-box', type: 'official', hide: true, }, '.feed-template': { name_zh: '文章列表', selector_type: 'class', name_en:'feed-template', type: 'official', hide: true, }, '.ad-box.ad-txt-box': { name_zh: '文章底部广告栏', selector_type: 'class', name_en:'ad-box.ad-txt-box', type: 'official', hide: true, }, '#csdn-copyright-footer': { name_zh: '版权信息栏', selector_type: 'id', name_en:'csdn-copyright-footer', type: 'official', hide: true, }, "[id^='dmp_ad_']": { name_zh: 'DMP广告栏', selector_type: 'attribute', name_en:'dmp_ad_', type: 'official', hide: true, } } }; return { // 返回所有选择器 getAllSelectors: () => { const host = window.location.hostname; const platform = Object.keys(CONFIG).find(k => host.includes(k)); return platform ? Object.entries(CONFIG[platform]).map(([selector, config]) => ({ name_zh: typeof config === 'object' ? config.name_zh : config, name_en: config?.name_en || selector, selector: selector, hide: config?.hide || false, type: config?.type || 'official', dynamic: config?.dynamic || false })) : []; }, // 根据中文名称查询,如果中文名称为空则返回所有选择器 getSelectorByChineseName: (name_zh) => { if (!name_zh) return this.getAllSelectors(); const host = window.location.hostname; const platform = Object.keys(CONFIG).find(k => host.includes(k)); if (!platform) return []; for (const [selector, config] of Object.entries(CONFIG[platform])) { const zhName = typeof config === 'object' ? config.name_zh : config; if (zhName === name_zh) { return [{ name_en: config?.name_en || selector, selector: selector, hide: config?.hide || false, type: config?.type || 'official', dynamic: config?.dynamic || false }]; } } return []; } }; })(); /* =============================== 元素配置 =============================== */ const EelementConfig = (() => { const CONFIG = { 'blog.csdn.net': { rightOffset: 850, topOffset: 110, }, 'www.jianshu.com': { rightOffset: 800, topOffset: 100, } }; return { // 获取指定配置 getConfig: () => { const { hostname } = window.location; const [platformKey] = Object.keys(CONFIG) .filter(k => hostname.includes(k)); return CONFIG[platformKey] || {}; } }; })(); /* =============================== 动态样式 =============================== */ // 【目录】右侧偏移量 const tocRightOffset = () => { const { rightOffset } = EelementConfig.getConfig(); return `max(20px, calc(50% - ${rightOffset}px))`; }; // 【目录】顶部偏移量 const tocTopOffset = () => { const { topOffset } = EelementConfig.getConfig(); return `${topOffset}px`; }; /* =============================== 样式常量 =============================== */ // 目录样式 const LOC_CONFIG = { // 目录样式 menu: { width: 'clamp(250px, 25vw, 300px)', /* 响应式宽度 */ right: tocRightOffset(), /* 动态右侧间距 */ top: tocTopOffset(), /* 动态顶部间距 */ background: '#f9f9f9', zIndex: 99999, borderRadius: '8px', boxShadow: '0 2px 12px rgba(0,0,0,0.1)' }, // 分割线样式 hr: { height: '2px', color: '#c7254e' }, // 目录项样式 listItem: { fontSize: '15px', hoverTransform: 'translateX(5px)' /* 平滑位移动画,向右移动5px */ } }; /* =============================== 工具函数库 =============================== */ const Utils = (() => { return { // 生成标准化类选择器 getTagSelector: (tag) => { const className = $(tag).attr('class'); if (!className) { console.error(`元素标签 ${tag} 没有 class 属性`); return ''; } return '.' + className.split(' ').join('.'); }, // 防抖函数,防止频繁触发事件 debounce: (func, delay) => { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => func.apply(this, args), delay); }; }, setupCopyGuard: () => { // 禁用文本选择 GM_addStyle(` * { -webkit-user-select: none!important; -moz-user-select: none!important; -ms-user-select: none!important; user-select: none!important; } .hljs { user-select: text!important; } `); // 阻止右键菜单 document.addEventListener('contextmenu', e => e.preventDefault()); // 拦截剪贴板事件 const handleClipboard = (e) => { const notice = '转载请注明出处:\n本文采用知识共享署名-非商业性使用 4.0 国际许可协议'; e.clipboardData.setData('text/plain', notice); e.preventDefault(); alert(notice); }; document.addEventListener('copy', handleClipboard); document.addEventListener('cut', handleClipboard); }, // 验证选择器类型是否有效 validateSelectorType: (selector) => { const validTypes = ['id', 'class', 'attribute']; const selectorRegex = { 'id': /^#([\w-]+|\[.+\])$/, 'class': /^\.([\w-]+|\[.+\])$/, 'attribute': /^\[.*?=['"].*?['"]\]$/ }; if (!validTypes.includes(selector.selector_type)) { console.error(`无效选择器类型: ${selector.selector_type},应为 id/class/attribute`); return false; } if (!selector.selector.match(selectorRegex[selector.selector_type])) { console.error(`无效 ${selector.selector_type} 选择器格式: ${selector.selector}`); return false; } return true; } }; })(); /* =============================== 核心功能模块 =============================== */ /* ======================== 目录生成模块 ======================== */ // 文章标题是否存在,默认为 true(存在) let isTitleExist = true; const TOCGenerator = (() => { // 获取排序后的唯一标题层级 function getSortedUniqueLevels(titles) { const levelSet = new Set(); titles.each(function() { const level = parseInt(this.tagName.substring(1)); levelSet.add(level); }); // 将 Set 转为数组并按 h1-h6 顺序排序 return Array.from(levelSet).sort((a, b) => a - b); } // 页面元素 const { tag: articleTag = 'article', } = elementTag.getTagByChineseName('文章标签'); const { name_en: sideMenuLocName = 'side-menu-toc', selector: sideMenuLocSelector = '#side-menu-toc' } = webElement.getSelectorByChineseName("目录主容器") || {}; const { name_en: menuLocOlName = 'menu_toc_ol', selector: menuLocOlSelector = '#menu_toc_ol' } = webElement.getSelectorByChineseName("目录列表") || {}; // 标题起始索引 let titleIndex = 1; // 目录标题 const LocTitle = "📖 内容导航"; // 获取所有标题,包含一级标题到六级标题 const $titles = $(articleTag).find('h1,h2,h3,h4,h5,h6'); if (!$titles.length) { isTitleExist = false; return false; } /* =============================== 事件监听 =============================== */ // 加载/调整大小/滚动时触发(严格模式) const optimizedScrollHandler = Utils.debounce(() => bindScrollEvents(true), 100); window.addEventListener('load', optimizedScrollHandler); window.addEventListener('resize', optimizedScrollHandler); window.addEventListener('scroll', optimizedScrollHandler); // 滚动时触发,用于同步菜单滚动位置 const optimizedSyncMenuScroll = Utils.debounce(() => syncMenuScroll(true), 100); window.addEventListener('scroll', optimizedSyncMenuScroll); /* =============================== 初始化目录结构 =============================== */ const initStructure = () => { // 文章容器 const $article = $(articleTag); if (!$article.length) { console.error(`[GreenWeb] [TOC] 未找到文章容器 ${articleTag}`); return false; } // 插入目录 $article.prepend(` <div id=${sideMenuLocName} style=" position: fixed; /* 固定定位 */ top: ${LOC_CONFIG.menu.top}; /* 距离顶部的距离 */ right: ${LOC_CONFIG.menu.right}; /* 距离右侧的距离 */ width: ${LOC_CONFIG.menu.width}; /* 宽度 */ background: ${LOC_CONFIG.menu.background}; /* 背景颜色 */ z-index: ${LOC_CONFIG.menu.zIndex}; /* 确保在其他元素之上 */ border-radius: ${LOC_CONFIG.menu.borderRadius}; /* 圆角 */ border-left: 1px solid #ccc; /* 左边框 */ box-sizing: border-box; /* 包含边框和内边距 */ box-shadow: ${LOC_CONFIG.menu.boxShadow}; /* 阴影 */ padding:15px 20px; /* 内边距 */ line-height: 1.3; /* 行高 */ max-width: calc(100vw - 1000px); /* 防溢出保护 */ max-height: 50vh; /* 最大高度 */ overflow-y: auto; /* 溢出时显示滚动条 */ "> <h2 style="margin:0 0 8px;font-size:18px;"> ${LocTitle} </h2> <hr style=" height: ${LOC_CONFIG.hr.height}; /* 线条粗细 */ background: ${LOC_CONFIG.hr.color}; /* 背景颜色 */ margin: 12px 0; /* 上下边距 */ border: none; /* 移除默认边框 */ "> <ol id=${menuLocOlName} style="list-style: none; margin: 0; padding: 0;"></ol> </div> `); return true; }; /* =============================== 生成目录项 =============================== */ const generateItems = () => { // 创建文档片段(内存中的临时容器) const fragment = document.createDocumentFragment(); const duplicateTitlesLevel = getSortedUniqueLevels($titles); $titles.each(function() { // 标题级别 const level = parseInt(this.tagName.substring(1)); // 标题内容 let title = $(this).text(); // 标题ID const titleId = `toc_${titleIndex++}`; // 为标题添加ID $(this).attr('id', titleId); // 当前标题在去重后的层级中的索引 const index = duplicateTitlesLevel.indexOf(level); let paddingLeft = 0; // 增加默认左边距,美化悬浮显示效果 // 所有标题层级相同,或标题层级与去重按顺序排序后的第一个层级相同时满足条件 if (duplicateTitlesLevel.length == 1 || index == 0) { paddingLeft = 10; } else { // 标题层级与去重按顺序排序后的最后一个层级相同时满足条件 paddingLeft = index * 20; } // 生成目录项 const $li = $(` <li class="${titleId}" style=" padding-left: ${paddingLeft}px; margin:6px 0; transition:all 0.2s; cursor:pointer; "> ${title} </li> `); if (!title.includes(LocTitle)) { // 将原生 DOM 元素添加到片段 fragment.appendChild($li[0]); } }); // 一次性添加所有目录项到文档片段,避免频繁操作DOM $(menuLocOlSelector).append(fragment); }; /* =============================== 绑定点击事件 =============================== */ const bindClickEvents = () => { $(menuLocOlSelector).on('click', 'li', function() { // 获取目标元素 const targetId = $(this).attr('class'); const targetElement = $(`#${targetId}`)[0]; // =============================== 处理页面滚动 =============================== // 多次点击同一目录项时,不做处理 if (!targetElement) { return ; } // 计算滚动位置 const elementHeight = targetElement.offsetHeight; const y = targetElement.getBoundingClientRect().top + window.pageYOffset - elementHeight - 10; // 滚动到目标位置 window.scrollTo({ top: y, behavior: 'smooth' }); // =============================== 处理点击状态 =============================== // 清除所有元素点击状态 $(`${menuLocOlSelector} li`).removeClass('active'); // 设置当前元素点击状态 $(this).addClass('active'); }); }; /* =============================== 绑定滚动事件 =============================== */ const bindScrollEvents = (strictMode) => { let currentId = ''; let minVisibleTop = Infinity; let maxInvisibleTop = -Infinity; let hiddenId = ''; Array.from($titles).forEach(title => { const rect = title.getBoundingClientRect(); const id = title.getAttribute('id'); // 严格模式判断逻辑 const isVisible = strictMode ? rect.top >= 10 && rect.bottom <= window.innerHeight : rect.top >= -rect.height; if (isVisible && rect.top < minVisibleTop) { minVisibleTop = rect.top; currentId = id; } else if (rect.top < 0 && rect.top > maxInvisibleTop) { maxInvisibleTop = rect.top; hiddenId = id; } }); currentId = currentId || hiddenId; const tocLinks = document.querySelectorAll(`${menuLocOlSelector} li`); // 更新激活状态 tocLinks.forEach(link => { const classList = link.className || ''; const firstClass = classList.split(/\s+/)[0] || ''; link.classList.toggle('active', firstClass === currentId); }); }; // 页面和长目录滚动同步 function syncMenuScroll() { const activeItem = $(`${menuLocOlSelector} li.active`)[0]; if (!activeItem) return; const menu = $(sideMenuLocSelector)[0]; const itemTop = activeItem.offsetTop; const itemHeight = activeItem.offsetHeight; const menuHeight = menu.clientHeight; // 计算目标滚动位置 const targetScrollTop = itemTop - (menuHeight - itemHeight) / 2; // 节流平滑滚动 if (!menu.scrollTimeout) { menu.scrollTimeout = setTimeout(() => { menu.scrollTo({ top: targetScrollTop, behavior: 'smooth' }); menu.scrollTimeout = null; }, 150); // 调整节流时间 (单位: 毫秒) } } /* =============================== 核心入口 =============================== */ return { init: () => { if (!initStructure()) return; generateItems(); bindClickEvents(); } }; })(); /* =============================== 广告管理模块 =============================== */ const AdManager = (() => { // 存储模块 const Storage = (() => { return { load: () => JSON.parse(localStorage.getItem('adSettings') || '{}'), save: (settings) => localStorage.setItem('adSettings', JSON.stringify(settings)) }; })(); const { name_en: adControlName = 'ad-control', } = webElement.getSelectorByChineseName("广告控制栏") || {}; // UI组件库 const ControlPanel = (() => { const create = () => { const { menu } = LOC_CONFIG; return $(` <div id=${adControlName} style=" position: fixed; bottom: 20px; right: ${menu.right}; width: ${menu.width}; background: ${menu.background}; box-shadow: ${menu.boxShadow}; border-radius: 8px; padding: 15px; min-height: 100px; max-height: 40vh; overflow-y: auto; z-index: 99999; display: block; "> <span class="close-btn" style=" position: absolute; right: 8px; top: 1px; cursor: pointer; font-size: 15px; color: #666; &:hover { color: #333 }"> × </span> <div style=" display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; "> <h3 style="margin: 0; font-size: 16px;"> 🛡️ 广告过滤器 <span id="ad-counter" style="float: right; font-size: 14px; color: #666;"> (<span id="filtered-count">0</span>/<span id="total-count">0</span>) </span> </h3> <button id="toggle-panel" style=" cursor: pointer; background: none; border: none; font-size: 20px; ">⬇️ </button> </div> <div id="filters" style="max-height: 25vh; overflow-y:auto;"> </div> <div style="margin-top: 10px; display: flex; gap: 10px;"> <button id="reset-default" style="flex: 1; padding: 5px;">恢复默认</button> <button id="toggle-all" style="flex: 1; padding: 5px;">全部</button> </div> </div>`); }; return { create }; })(); // 核心逻辑 const Core = (() => { const setupEventListeners = (panel) => { }; return { init: () => { if (window.location.hostname !== 'blog.csdn.net') return; // 创建广告控制面板 window.panel = ControlPanel.create(); $('body').append(window.panel); setupEventListeners(window.panel); } }; })(); return { init: Core.init }; })(); // 广告计数器更新函数 const updateAdCounter = (() => { return { init: () => { const counters = webElement.getAllSelectors(); const total = counters.filter(c => { const el = document.querySelector(c.selector); return el && c.type === 'official'; }).length; const filtered = counters.filter(c => { const el = document.querySelector(c.selector); return el && (el.style.display === 'none' || el.offsetParent === null); }).length; $('#filtered-count').text(filtered); $('#total-count').text(total); } }; })(); const createControlPanel = () => { const panel = window.panel; // 从 localStorage 加载用户设置 const userSettings = JSON.parse(localStorage.getItem('adSettings') || '{}'); const elements = webElement.getAllSelectors() ?? []; // =============================== 【CSDN】创建控制面板 =============================== elements.forEach((element) => { const selector = element.selector; const el = document.querySelector(selector); if (!el) { return; } const name_zh = element.name_zh; const hide = element.hide; // 如果 localStorage 中没有设置,则使用默认值 const isEnabled = userSettings[selector] !== undefined ? userSettings[selector] : hide; panel.find('#filters').append(` <label style=" display:flex; align-items:center; gap:8px; padding:5px; cursor:pointer; "> <input type="checkbox" ${isEnabled ? 'checked' : ''} data-selector="${selector}"> <span style="font-size:13px;">${selector}『${name_zh}』</span> </label> `); }); // =============================== 【CSDN】面板切换功能 =============================== panel.find('#toggle-panel').click(() => { panel.toggleClass('collapsed'); $('#filters').slideToggle(); $('#toggle-panel').text(panel.hasClass('collapsed') ? '⬆️' : '⬇️'); }); // =============================== 【CSDN】控制面板事件 =============================== // 保存设置到 localStorage panel.on('change', 'input[type="checkbox"]', function() { const selector = $(this).data('selector'); userSettings[selector] = $(this).prop('checked'); localStorage.setItem('adSettings', JSON.stringify(userSettings)); console.log(`[GreenWeb] [AdFilter] 已保存设置: ${selector} => ${userSettings[selector]}`); $(selector).css('display', userSettings[selector] ? 'none' : 'block'); updateAdCounter.init(); }); // 重置默认设置 panel.find('#reset-default').click(() => { localStorage.removeItem('adSettings'); // 刷新页面 location.reload(); }); // 全选/全不选 panel.find('#toggle-all').click(() => { const checkboxes = panel.find('input[type="checkbox"]'); console.log(checkboxes); const allChecked = checkboxes.toArray().every(cb => cb.checked); checkboxes.prop('checked', !allChecked).trigger('change'); updateAdCounter.init(); }); // 绑定广告关闭按钮点击事件 $('#ad-control .close-btn').on('click', function() { $(this).closest('#ad-control').hide(); }); return panel; }; const removeAds = () => { // 移除简书广告 if (window.location.hostname === 'www.jianshu.com') { // =============================== 【简书】移除侧边栏标签 =============================== const { tag: asideTag = 'aside', hide: hideAsideTag = false } = elementTag.getTagByChineseName('侧边栏标签'); const asideSelector = Utils.getTagSelector(asideTag); if (!asideSelector) { console.error(`[GreenWeb] [TOC] 未找到侧边栏标签 ${asideTag} 对应的选择器`); return false; } if (hideAsideTag) { $(asideSelector).css('display','none'); } // =============================== 【简书】移除其他标签 =============================== const { tag: articleTag = 'article', } = elementTag.getTagByChineseName('文章标签'); // 获取文章标签的父级元素 const $articleParent = $(articleTag).parent(); // 隐藏文章标签父级元素的所有同级节点 $articleParent.siblings().hide(); } else if (window.location.hostname === 'blog.csdn.net') { // =============================== 【CSDN】批量移除元素 =============================== const elements = webElement.getAllSelectors() ?? []; elements.forEach(element => { if (element.hide) { $(element.selector).hide(); } }); // =============================== 【CSDN】移除复制保护 =============================== $('.hljs-button.signin') .removeClass('signin') // 移除 signin 类 .removeAttr('onclick') // 移除 onclick 属性 .attr('data-title', '点击复制') // 设置新的 data-title 属性 .on('click', async function() { // 新增点击事件,异步处理 try { const $button = $(this); const $codeBlock = $button.closest('.hljs').find('code') || $button.siblings('.code-content'); // 同时写入纯文本和富文本格式 const htmlContent = $codeBlock.html() .replace(/</g, '<') // 保持 HTML 转义 .replace(/\n/g, '<br>') // 保留换行 + `<style>${$codeBlock.attr('style')}</style>`; // 保留代码样式 const textContent = $codeBlock.text() .replace(/\n\s+/g, '\n') // 压缩缩进 .replace(/\t/g, ' '); // 替换制表符 await navigator.clipboard.write([ new ClipboardItem({ 'text/plain': new Blob([textContent], { type: 'text/plain' }), 'text/html': new Blob([ `<pre style="${$codeBlock.attr('style')}" class="${$codeBlock.attr('class')}">${htmlContent}</pre>` ], { type: 'text/html' }) }) ]); $button.attr('data-title', '✅ 已复制'); setTimeout(() => $button.attr('data-title', '点击复制'), 2000); } catch (err) { console.error('复制失败:', err); $button.attr('data-title', '❌ 复制失败'); setTimeout(() => $button.attr('data-title', '点击复制'), 1500); } });; }; }; /* =============================== 样式管理模块 =============================== */ GM_addStyle(` /* =============================== 广告控制面板 =============================== */ #ad-control.collapsed { height: 40px; overflow: hidden; } #ad-control button { background: #f0f0f0; border-radius: 4px; transition: all 0.2s; } #ad-control button:hover { background: #e0e0e0; } #filters label:hover { background: #f5f5f5; } @media (prefers-color-scheme: dark) { #ad-control { background: #2d2d2d !important; color: #e0e0e0; } #ad-control button { background: #404040; color: #fff; } #filters label:hover { background: #3d3d3d; } } /* =============================== 目录 =============================== */ #menu_toc_ol li { font-size: ${LOC_CONFIG.listItem.fontSize}; /* 字体大小 */ cursor: pointer; /* 鼠标悬停时的光标样式 */ transition: all 0.2s; /* 过渡效果 */ border-radius: 4px; /* 圆角 */ padding-top: 4px; /* 上边距 */ padding-bottom: 4px; /* 下边距 */ } #menu_toc_ol li:hover { background: #e7b948; /* 悬停时的背景颜色 */ text-decoration: underline; /* 悬停时添加下划线 */ transform: ${LOC_CONFIG.listItem.hoverTransform}; /* 悬停时的动画效果 */ } #menu_toc_ol li.active { color: #1902c0; /* 点击时的文字颜色 */ font-weight: 500; /* 加粗显示 */ background: #e8f0fe; /* 点击时的背景颜色 */ } @media (prefers-color-scheme: dark) { /* 深色模式 */ #side-menu-toc { background: #2d2d2d !important; color: #e0e0e0; } #side-menu-toc h2 { color: #e0e0e0 !important; } #menu_toc_ol li.active { background: #88969f; } #menu_toc_ol li:hover { background: #a4c99e; color: #000000; } } /* 响应式处理 */ @media (max-width: 1400px) { #side-menu-toc, #ad-control { display: none !important; } } `); /* =============================== 主执行流程 =============================== */ (() => { 'use strict'; /* =============================== 移除广告 =============================== */ (() => { try { removeAds(); console.log('[GreenWeb] [ADB] ✅ 广告已净化'); } catch (e) { console.error('[GreenWeb] [ADB] 💥 广告净化模块异常 - ', e); } })(); /* =============================== 广告控制 =============================== */ (() => { try { if (window.location.hostname === 'blog.csdn.net') { AdManager.init(); $('body').append(createControlPanel()); updateAdCounter.init(); console.log('[GreenWeb] [ADB] ✅ 广告控制面板初始化完成'); } } catch (e) { console.error('[GreenWeb] [ADB] 💥 广告控制模块异常 - ', e); } })(); /* =============================== 生成目录 =============================== */ const shouldGenerateTOC = (() => { try { if (!isTitleExist) { console.info('[GreenWeb] [TOC] 文章标题不存在,跳过目录生成'); return false; } return true; } catch (e) { console.error('[GreenWeb] [TOC] 💥 目录预检异常 - ', e); return false; } })(); (() => { try { if (!shouldGenerateTOC) return; console.log("[GreenWeb] [TOC] 开始生成目录... ⏳"); TOCGenerator.init(); console.log('[GreenWeb] [TOC] ✅ 目录生成完成,请尽情享受吧!🎉 🍺'); } catch (e) { console.error('[GreenWeb] [TOC] 💥 目录生成模块异常 - ', e); } })(); })();