Firefox扩展:解决YouTube切换视频时函数仅触发一次的问题
解决YouTube扩展切换视频时无法触发事件的问题
你的问题很典型——YouTube现在是单页应用(SPA),很多路由更新用的是HTML5 History API的pushState/replaceState,而不是传统的hashchange,所以只监听hashchange自然只会触发一次。另外你用的DOM选择器依赖nth-child,也容易因为页面结构微调失效。下面给你两个更可靠的解决方案:
方案1:监听History状态变化(覆盖所有路由更新场景)
YouTube切换视频时,大多会调用history.pushState更新URL路径,而非修改hash。我们可以包装原生的pushState和replaceState方法,触发自定义事件来捕捉这些变化:
// 包装History API方法,触发自定义状态变化事件 (function() { const originalPush = history.pushState; const originalReplace = history.replaceState; history.pushState = function(...args) { originalPush.apply(this, args); window.dispatchEvent(new Event('routeChange')); }; history.replaceState = function(...args) { originalReplace.apply(this, args); window.dispatchEvent(new Event('routeChange')); }; // 监听自定义路由变化事件,同时保留hashchange兼容旧场景 ['routeChange', 'hashchange'].forEach(eventName => { window.addEventListener(eventName, () => { // 加小延迟确保DOM已更新 setTimeout(logVideoDetails, 400); }); }); })(); // 封装视频信息获取函数,用更稳定的选择器 function logVideoDetails() { const title = document.querySelector('h1.title yt-formatted-string')?.textContent || '标题未加载'; const likeBtn = document.querySelector('ytd-like-button-renderer#top-level-buttons-computed'); const dislikeBtn = document.querySelector('ytd-dislike-button-renderer#top-level-buttons-computed'); const likeCount = likeBtn?.getAttribute('aria-label') || '点赞数未加载'; const dislikeCount = dislikeBtn?.getAttribute('aria-label') || '点踩数未加载'; console.log(`视频标题: ${title}\n点赞信息: ${likeCount}\n点踩信息: ${dislikeCount}\n`); } // 页面初始加载时先执行一次 logVideoDetails();
方案2:用MutationObserver监听视频内容变化(更精准)
如果不想监听路由,直接监听视频核心元素的变化更可靠——切换视频时,标题、点赞按钮这些元素一定会更新。用MutationObserver可以实时捕捉DOM变化:
function logVideoDetails() { const title = document.querySelector('h1.title yt-formatted-string')?.textContent || '标题未加载'; const likeBtn = document.querySelector('ytd-like-button-renderer#top-level-buttons-computed'); const dislikeBtn = document.querySelector('ytd-dislike-button-renderer#top-level-buttons-computed'); const likeCount = likeBtn?.getAttribute('aria-label') || '点赞数未加载'; const dislikeCount = dislikeBtn?.getAttribute('aria-label') || '点踩数未加载'; console.log(`视频标题: ${title}\n点赞信息: ${likeCount}\n点踩信息: ${dislikeCount}\n`); } // 初始执行一次 logVideoDetails(); // 监听标题容器的DOM变化 const titleContainer = document.querySelector('#title'); if (titleContainer) { const observer = new MutationObserver((mutations) => { // 当标题内容或子元素变化时触发 if (mutations.some(m => m.type === 'childList' || m.type === 'characterData')) { setTimeout(logVideoDetails, 300); } }); observer.observe(titleContainer, { childList: true, subtree: true, characterData: true }); }
关键改进点:
- 替换不稳定的选择器:去掉了依赖
nth-child的选择器,改用组件ID(比如#top-level-buttons-computed)和明确的标签名,避免页面结构微调导致选择器失效。 - 处理DOM加载延迟:用
setTimeout给页面一点时间更新DOM,确保能获取到最新的元素信息。 - 覆盖所有场景:两种方案分别从路由变化和内容变化入手,都能解决你遇到的“从推荐栏选新视频不触发”的问题,其中MutationObserver方案更精准,因为它直接对应视频内容的更新。
内容的提问来源于stack exchange,提问作者danielleontiev
相关产品推荐
相关产品推荐

