Chrome扩展开发:如何拦截YouTube缩略图更新并实时替换内容?
解决YouTube SPA导航后缩略图时长替换内容不更新的问题
我正在开发一款Chrome扩展,目标是把YouTube推荐视频缩略图上的时长(比如14:32)替换成对应的频道名称(比如PewDiePie)。单次替换功能已经实现,但遇到一个问题:当通过点击推荐视频导航到新页面时,缩略图已经切换到新视频,但显示的还是旧的频道名称,内容没有更新。
问题根源
YouTube是单页应用(SPA),页面导航时不会完全刷新DOM,而是复用现有节点。原来的MutationObserver只监听了新增节点(如下滑加载新内容),但对于已经存在的缩略图节点,当它们关联的视频信息更新时,不会触发新增节点的监听逻辑,导致旧的频道名残留。
解决方案
需要从三个方面调整代码:
- 优化频道名的获取逻辑,确保能稳定获取当前缩略图对应的最新频道名
- 扩展
MutationObserver的监听范围,覆盖现有节点的属性、内容变化 - 监听SPA导航的URL变化,触发全局重新更新所有缩略图
修改后的代码
TypeScript版本
function processNode(node: HTMLElement) { // 从缩略图所在的视频卡片节点中获取频道名,层级更稳定 const videoCard = node.closest('ytd-video-renderer'); if (!videoCard) return; const channelName = videoCard.querySelector('ytd-channel-name #text'); if (channelName?.textContent) { node.textContent = channelName.textContent; } } function replaceAllThumbnailTimes(): void { document.querySelectorAll('span.ytd-thumbnail-overlay-time-status-renderer#text') .forEach(node => processNode(node as HTMLElement)); } function setupMutationObserver(): void { const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { // 处理新增节点 for (const node of mutation.addedNodes) { if (node instanceof HTMLElement && node.matches('span.ytd-thumbnail-overlay-time-status-renderer#text')) { processNode(node); } } // 处理现有节点的属性/内容变化:检查是否是时间节点或其父节点变化 if (mutation.target instanceof HTMLElement) { // 如果变化的是时间节点本身,直接处理 if (mutation.target.matches('span.ytd-thumbnail-overlay-time-status-renderer#text')) { processNode(mutation.target); } // 如果变化的是视频卡片或频道名元素,找到其内部的时间节点处理 else if (mutation.target.closest('ytd-video-renderer')) { const timeNode = mutation.target.closest('ytd-video-renderer')?.querySelector('span.ytd-thumbnail-overlay-time-status-renderer#text'); if (timeNode instanceof HTMLElement) { processNode(timeNode); } } } } }); observer.observe(document.body, { childList: true, subtree: true, characterData: true, attributes: true, attributeFilter: ['href', 'title'] // 监听关键属性变化,减少不必要触发 }); } function setupNavigationListener(): void { // 监听SPA导航的URL变化 let currentUrl = window.location.href; setInterval(() => { const newUrl = window.location.href; if (newUrl !== currentUrl) { currentUrl = newUrl; // 延迟一点执行,确保页面内容更新完成 setTimeout(replaceAllThumbnailTimes, 500); } }, 200); } // 初始化执行 replaceAllThumbnailTimes(); setupMutationObserver(); setupNavigationListener();
JavaScript版本(可直接在控制台运行)
function processNode(node) { const videoCard = node.closest('ytd-video-renderer'); if (!videoCard) return; const channelName = videoCard.querySelector('ytd-channel-name #text'); if (channelName?.textContent) { node.textContent = channelName.textContent; } } function replaceAllThumbnailTimes() { document.querySelectorAll('span.ytd-thumbnail-overlay-time-status-renderer#text') .forEach(node => processNode(node)); } function setupMutationObserver() { const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { for (const node of mutation.addedNodes) { if (node instanceof HTMLElement && node.matches('span.ytd-thumbnail-overlay-time-status-renderer#text')) { processNode(node); } } if (mutation.target instanceof HTMLElement) { if (mutation.target.matches('span.ytd-thumbnail-overlay-time-status-renderer#text')) { processNode(mutation.target); } else if (mutation.target.closest('ytd-video-renderer')) { const timeNode = mutation.target.closest('ytd-video-renderer')?.querySelector('span.ytd-thumbnail-overlay-time-status-renderer#text'); if (timeNode instanceof HTMLElement) { processNode(timeNode); } } } } }); observer.observe(document.body, { childList: true, subtree: true, characterData: true, attributes: true, attributeFilter: ['href', 'title'] }); } function setupNavigationListener() { let currentUrl = window.location.href; setInterval(() => { const newUrl = window.location.href; if (newUrl !== currentUrl) { currentUrl = newUrl; setTimeout(replaceAllThumbnailTimes, 500); } }, 200); } replaceAllThumbnailTimes(); setupMutationObserver(); setupNavigationListener();
关键改进点
- 稳定的选择器:通过
ytd-video-renderer层级获取频道名,避免依赖不稳定的父元素层级,适配YouTube DOM结构变化。 - 扩展Observer监听:不仅处理新增节点,还监听现有节点的属性和内容变化,当视频卡片或时间节点本身变化时重新处理。
- SPA导航监听:通过定时检查URL变化,触发全局更新,确保页面切换后所有缩略图都能同步最新内容。
内容的提问来源于stack exchange,提问作者dcferreira
相关产品推荐
相关产品推荐

