YouTube新界面评论处理Greasemonkey脚本缓存问题求助
解决YouTube评论缓存复用导致的用户脚本失效问题
看起来你精准踩到了YouTube Polymer界面的DOM复用坑——它会缓存评论元素,导航到新视频或返回时直接复用旧DOM节点,只更新内部数据,而你的脚本只在元素首次出现时处理一次,自然会出现内容混乱的情况。
核心问题分析
YouTube的评论渲染器(ytd-comment-renderer)采用了DOM复用缓存机制:
- 首次加载视频时创建评论元素,处理后你用
data('alreadyFound')标记为已处理; - 导航到其他视频时,这些元素不会被销毁,而是被隐藏并缓存;
- 返回或打开新视频时,缓存的元素会被重新插入页面,同时填充新的评论数据,但你的脚本因为标记了
alreadyFound,不会再处理这些元素,导致旧的处理结果和新数据混合。
解决方案思路
我们需要从「检测元素新增」转向「监听元素内容变化」,同时结合页面导航事件重置状态:
- 使用
MutationObserver替代waitForKeyElements,既监听新评论元素的添加,也监听已有元素的内容更新; - 用弱引用存储已处理的评论内容哈希,避免重复处理同一内容,但允许内容变化时重新处理;
- 在
yt-navigate-finish事件触发时,清空已处理记录并重新初始化监听逻辑。
修复后的完整脚本
// ==UserScript== // @name TEST2 Fixed // @include https://*youtube.com/* // @require https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js // @grant none // @run-at document-start // ==/UserScript== // 用WeakMap存储已处理的评论内容,避免内存泄漏 const processedComments = new WeakMap(); function processComment(jNode) { const authorTextSpan = $(jNode).find("#author-text span.style-scope.ytd-comment-renderer")[0]; if (!authorTextSpan) return; const authorName = authorTextSpan.textContent.trim(); // 用作者名作为内容标识(也可以用更复杂的哈希) const contentKey = authorName; // 如果内容未变化,跳过处理 if (processedComments.get(jNode[0]) === contentKey) return; // 先移除之前添加的自定义元素,避免重复追加 $(jNode).find("#author-text span:contains('===')").remove(); const newSpan = document.createElement('span'); newSpan.innerHTML = ' <font color="red">===</font> ' + authorName; $(jNode).find("#author-text").append(newSpan); // 更新已处理记录 processedComments.set(jNode[0], contentKey); } function setupCommentObserver() { // 清理之前的监听器 if (window.commentObserver) { window.commentObserver.disconnect(); } const commentContainer = document.querySelector('#main'); if (!commentContainer) { // 容器未加载时延迟重试 setTimeout(setupCommentObserver, 300); return; } // 先处理页面上已存在的评论 $('#main .style-scope ytd-comment-renderer').each(function() { processComment($(this)); // 为每个已有评论添加内容监听器 setupContentObserver(this); }); // 监听评论区新增元素 window.commentObserver = new MutationObserver((mutations) => { mutations.forEach((mutation) => { mutation.addedNodes.forEach((node) => { if (node.matches && node.matches('.style-scope ytd-comment-renderer')) { processComment($(node)); setupContentObserver(node); } }); }); }); window.commentObserver.observe(commentContainer, { childList: true, subtree: true }); } function setupContentObserver(commentNode) { const authorText = commentNode.querySelector('#author-text'); if (!authorText) return; // 监听评论元素内部的内容变化(文本更新、子节点变化) const contentObserver = new MutationObserver(() => { processComment($(commentNode)); }); contentObserver.observe(authorText, { childList: true, subtree: true, characterData: true }); // 将监听器绑定到节点上,方便后续清理 commentNode.contentObserver = contentObserver; } // 页面导航完成后重置监听状态 document.addEventListener('yt-navigate-finish', () => { processedComments.clear(); setupCommentObserver(); }); // 初始化监听 setupCommentObserver();
关键改进点
- 双层MutationObserver:
- 外层监听评论区容器,捕获新添加的评论元素;
- 内层监听单个评论的作者文本节点,捕获内容更新。
- 内容变化判断:通过WeakMap存储评论内容的标识,只在内容真正变化时重新处理,避免不必要的重复操作。
- 导航后重置:在页面导航完成后清空已处理记录,确保新视频的评论能被正确处理。
测试一下这个脚本:导航到新视频、返回旧视频,评论的自定义内容都会和当前作者名匹配,不会再出现混乱的情况。
内容的提问来源于stack exchange,提问作者choin




