You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

YouTube新界面评论处理Greasemonkey脚本缓存问题求助

解决YouTube评论缓存复用导致的用户脚本失效问题

看起来你精准踩到了YouTube Polymer界面的DOM复用坑——它会缓存评论元素,导航到新视频或返回时直接复用旧DOM节点,只更新内部数据,而你的脚本只在元素首次出现时处理一次,自然会出现内容混乱的情况。

核心问题分析

YouTube的评论渲染器(ytd-comment-renderer)采用了DOM复用缓存机制

  1. 首次加载视频时创建评论元素,处理后你用data('alreadyFound')标记为已处理;
  2. 导航到其他视频时,这些元素不会被销毁,而是被隐藏并缓存;
  3. 返回或打开新视频时,缓存的元素会被重新插入页面,同时填充新的评论数据,但你的脚本因为标记了alreadyFound,不会再处理这些元素,导致旧的处理结果和新数据混合。

解决方案思路

我们需要从「检测元素新增」转向「监听元素内容变化」,同时结合页面导航事件重置状态:

  1. 使用MutationObserver替代waitForKeyElements,既监听新评论元素的添加,也监听已有元素的内容更新;
  2. 用弱引用存储已处理的评论内容哈希,避免重复处理同一内容,但允许内容变化时重新处理;
  3. 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();

关键改进点

  1. 双层MutationObserver
    • 外层监听评论区容器,捕获新添加的评论元素;
    • 内层监听单个评论的作者文本节点,捕获内容更新。
  2. 内容变化判断:通过WeakMap存储评论内容的标识,只在内容真正变化时重新处理,避免不必要的重复操作。
  3. 导航后重置:在页面导航完成后清空已处理记录,确保新视频的评论能被正确处理。

测试一下这个脚本:导航到新视频、返回旧视频,评论的自定义内容都会和当前作者名匹配,不会再出现混乱的情况。

内容的提问来源于stack exchange,提问作者choin

火山引擎 最新活动