Tampermonkey脚本复选框仅在个人资料页显示问题求助
Tampermonkey脚本复选框仅在Kbin个人资料页显示的问题排查与解决
问题原因
- DOM加载时机未同步:脚本直接在页面初始化阶段执行,部分页面的header菜单元素尚未完成渲染,导致
document.querySelectorAll('#header.header menu')[1]返回undefined,后续调用headerMenu.appendChild()时抛出错误,中断复选框创建流程。 - 选择器依赖固定索引:不同Kbin页面的header菜单结构存在差异,
querySelectorAll('menu')[1]这种依赖固定位置的选择器,在非个人资料页无法匹配到目标菜单元素,导致操作失效。 - 缺乏元素存在性校验:代码未检查
headerMenu等核心元素是否存在,一旦选择器匹配失败,直接执行DOM操作会引发错误,导致复选框无法创建。
解决方法
1. 等待DOM完全加载后执行逻辑
通过DOMContentLoaded事件确保页面核心元素渲染完成后,再执行复选框创建和声望移除逻辑。
2. 使用稳健的选择器
避免依赖固定索引,改用结构或属性选择器定位目标菜单,例如#header.header menu:last-of-type适配不同页面的菜单布局。
3. 添加元素存在性校验
所有DOM操作前先检查元素是否存在,避免因元素缺失引发的错误。
修改后的完整脚本
// ==UserScript== // @name Remove Reputation // @namespace http://tampermonkey.net/ // @version 0.1 // @description Remove reputation points from user's profile. Navigate Kbin unbiased. // @author minnieo // @match https://kbin.social/* // @icon https://www.google.com/s2/favicons?sz=64&domain=kbin.social // @grant none // @license MIT // ==/UserScript== let repEnabled = localStorage.getItem('repEnabled') === 'true'; function reputation() { if (!repEnabled) return; // 校验元素存在性后再操作 const popover = document.getElementById('popover'); if (popover) { popover.addEventListener('openPopover', (e) => { const liElement = e.target.querySelector('li:nth-child(2)'); liElement?.remove(); }); } const userInfoLi = document.querySelector('.section.user-info .info li:nth-child(2)'); userInfoLi?.remove(); } // 等待DOM加载完成后执行核心逻辑 document.addEventListener('DOMContentLoaded', () => { reputation(); // 定位目标header菜单(改用last-of-type避免索引依赖) const headerMenu = document.querySelector('#header.header menu:last-of-type'); if (!headerMenu) { console.warn('目标header菜单未找到'); return; } // 创建复选框元素 const repToggleCont = document.createElement('li'); const repToggle = document.createElement('a'); repToggle.className = repEnabled ? 'fa-solid fa-square-check' : 'fa-solid fa-square'; repToggle.title = repEnabled ? 'Show reputation (reloads page)' : 'Hide reputation'; repToggleCont.appendChild(repToggle); // 插入到用户下拉菜单之后(若存在),否则插入到菜单末尾 const profileDropdown = headerMenu.querySelector('li.dropdown:last-of-type'); profileDropdown ? profileDropdown.after(repToggleCont) : headerMenu.appendChild(repToggleCont); // 复选框点击事件 repToggle.addEventListener('click', () => { repEnabled = !repEnabled; localStorage.setItem('repEnabled', repEnabled); if (repEnabled) { repToggle.classList.replace('fa-square', 'fa-square-check'); reputation(); } else { repToggle.classList.replace('fa-square-check', 'fa-square'); location.reload(); } }); });
可选:适配动态加载的header
如果页面存在异步加载的header内容,可引入MutationObserver监听元素变化:
// 监听header动态加载 const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.addedNodes.length) { const headerMenu = document.querySelector('#header.header menu:last-of-type'); const existingToggle = document.querySelector('.fa-square, .fa-square-check'); if (headerMenu && !existingToggle) { // 此处插入复选框创建逻辑(同上述代码) observer.disconnect(); // 找到元素后停止监听 } } } }); observer.observe(document.body, { childList: true, subtree: true });
内容的提问来源于stack exchange,提问作者Minnie Minnie
相关产品推荐
相关产品推荐

