YouTube内容脚本页面跳转后元素内容未更新的问题求助
YouTube内容脚本页面跳转后元素内容未更新的问题求助
看起来你遇到了YouTube单页应用(SPA)机制带来的典型问题——YouTube不会在页面跳转时刷新整个页面,很多DOM元素是被复用的(比如播放器区域、视频信息栏这些核心元素),你的myQuerySelector只检测到元素存在,却没确认这些元素的内容已经更新为当前视频的信息,自然就拿到旧数据了。哪怕你跳去频道页再回来,那些复用的元素可能只是被隐藏/显示,内容没及时同步,所以才会出现这种奇怪的情况。
核心解决思路:从「检测元素存在」升级为「检测元素内容对应当前视频」
你需要在获取到元素后,额外验证它的内容是否属于当前的newId,而不是只看元素有没有。另外,也可以利用MutationObserver监听元素的内容变化,比轮询更高效准确。
方案1:给myQuerySelector加内容验证逻辑
修改你的myQuerySelector,让它可以接收一个验证函数,确保拿到的元素内容是对应当前视频的:
async function myQuerySelector(selector, all = false, maxDelay = 10000, validateFn = null) { const delay = 50; //ms let totalDelay = 0; let elements = []; while (totalDelay <= maxDelay) { elements = Array.from(document.querySelectorAll(selector)); // 如果有验证函数,还要检查元素是否符合要求 if (elements.length && (!validateFn || validateFn(elements))) { break; } await pause(delay); totalDelay += delay; } console.log(`Found after ${totalDelay}ms`, selector); return all ? elements : elements[0]; }
然后在handleVideo里调用时,传入验证函数,比如验证视频标题的元素是否和当前视频匹配(或者检查元素里的视频ID相关属性):
async function handleVideo(currentVideoId) { // 比如获取视频标题元素,验证它对应的视频ID是否正确 const titleElement = await myQuerySelector('h1.title', false, 10000, (elements) => { // 这里可以根据实际情况写验证逻辑,比如检查视频元素的src是否包含当前ID const videoElement = document.querySelector('video'); return videoElement && videoElement.src.includes(currentVideoId); }); // 后续操作titleElement就放心了 }
方案2:用MutationObserver监听元素内容变化
对于核心元素(比如视频信息容器),可以监听它的子节点变化,直到内容更新后再执行逻辑,比轮询更高效:
async function waitForElementUpdate(selector, validateFn, maxDelay = 10000) { return new Promise((resolve, reject) => { const element = document.querySelector(selector); if (!element) { // 先等元素出现 const checkInterval = setInterval(() => { const el = document.querySelector(selector); if (el) { clearInterval(checkInterval); startObserving(el); } }, 50); setTimeout(() => { clearInterval(checkInterval); reject(new Error('Element not found within timeout')); }, maxDelay); } else { startObserving(element); } function startObserving(el) { // 先检查当前内容是否符合要求 if (validateFn(el)) { resolve(el); return; } const observer = new MutationObserver((mutations) => { if (validateFn(el)) { observer.disconnect(); resolve(el); } }); observer.observe(el, { childList: true, subtree: true, characterData: true }); // 设置超时 setTimeout(() => { observer.disconnect(); reject(new Error('Element update timeout')); }, maxDelay); } }); }
在handleVideo里这样用:
async function handleVideo(currentVideoId) { try { const titleElement = await waitForElementUpdate('h1.title', (el) => { const video = document.querySelector('video'); return video && video.src.includes(currentVideoId); }); // 处理元素 } catch (err) { console.error('Wait for element update failed:', err); } }
额外优化:改进视频ID的检测逻辑
你当前的轮询getVideoId的方式可以优化,比如监听YouTube的路由变化事件,而不是每隔50ms轮询,这样能更精准地捕捉视频切换的时机:
// 监听SPA路由变化(YouTube用history.pushState) let lastPath = window.location.pathname; window.addEventListener('popstate', checkVideoChange); // 重写pushState来监听 const originalPushState = history.pushState; history.pushState = function(...args) { originalPushState.apply(this, args); checkVideoChange(); }; async function checkVideoChange() { const newPath = window.location.pathname; if (newPath !== lastPath) { lastPath = newPath; const newId = await getVideoId(true); if (newId) { console.log('New id', newId); await handleVideo(newId); // 把当前id传进去 id = newId; } } }
这样能在路由切换的第一时间触发检测,比轮询更高效,也能减少不必要的循环。
备注:内容来源于stack exchange,提问作者AlwaysLearning
相关产品推荐
相关产品推荐

