2022年如何在YouTube页面导航时运行Chrome扩展内容脚本?
解决YouTube SPA导航时内容脚本不自动运行的问题
YouTube作为单页应用(SPA),页面导航依赖history.pushState/replaceState而非页面重载,导致内容脚本仅在首次刷新时执行。以下是两种实用解决方案:
方案1:监听History API变化(推荐)
直接捕获YouTube的导航行为,通过重写History方法触发自定义导航事件,在事件回调中执行你的逻辑:
// 保存History原始方法 const originalPushState = history.pushState; const originalReplaceState = history.replaceState; // 重写pushState,触发导航事件 history.pushState = function(...args) { const result = originalPushState.apply(this, args); window.dispatchEvent(new CustomEvent('yt-navigate', { detail: { url: window.location.href } })); return result; }; // 重写replaceState,触发导航事件 history.replaceState = function(...args) { const result = originalReplaceState.apply(this, args); window.dispatchEvent(new CustomEvent('yt-navigate', { detail: { url: window.location.href } })); return result; }; // 监听浏览器前进/后退的popstate事件 window.addEventListener('popstate', () => { window.dispatchEvent(new CustomEvent('yt-navigate', { detail: { url: window.location.href } })); }); // 核心逻辑:注入元素并执行代码 function handleNavigation() { // 清理旧元素,避免重复注入 const existingElement = document.getElementById('a'); if (existingElement) existingElement.remove(); // 等待player元素加载完成 const checkPlayerInterval = setInterval(() => { const player = Array.from(document.querySelectorAll('#player')).pop(); if (player) { clearInterval(checkPlayerInterval); player.insertAdjacentHTML("afterend", `<div id="a" style="color:red">Code</div>`); // 这里添加你的其他业务代码 } }, 100); } // 初始页面加载时执行 window.addEventListener('load', handleNavigation); // 导航变化时执行 window.addEventListener('yt-navigate', handleNavigation);
方案2:优化现有MutationObserver实现
缩小监听范围,结合URL变化判断,减少不必要的回调触发:
let currentUrl = window.location.href; function injectScript() { const player = Array.from(document.querySelectorAll('#player')).pop(); if (!player) return; const existingElement = document.getElementById('a'); // URL变化时移除旧元素,避免重复 if (existingElement) { if (window.location.href !== currentUrl) { existingElement.remove(); } else { return; } } player.insertAdjacentHTML("afterend", `<div id="a" style="color:red">Code</div>`); currentUrl = window.location.href; // 这里添加你的其他业务代码 } // 监听URL变化(同方案1的History重写逻辑) const originalPushState = history.pushState; const originalReplaceState = history.replaceState; history.pushState = function(...args) { const result = originalPushState.apply(this, args); injectScript(); return result; }; history.replaceState = function(...args) { const result = originalReplaceState.apply(this, args); injectScript(); return result; }; window.addEventListener('popstate', injectScript); // 初始加载+监听核心容器变化 window.addEventListener('load', () => { injectScript(); // 监听YouTube页面的核心容器,缩小监听范围 const observerTarget = document.querySelector('#page-manager') || document.body; const mutationObserver = new MutationObserver((mutations) => { // 仅当有新增节点时检查player if (mutations.some(m => m.addedNodes.length > 0)) { injectScript(); } }); mutationObserver.observe(observerTarget, { childList: true, subtree: true }); });
关键注意点
- 避免重复注入:每次导航后先检查并清理旧元素,或通过URL变化判断是否需要重新执行。
- 元素加载时机:YouTube的player是动态渲染的,需通过定时器或精准监听确保元素存在后再注入。
- 性能优化:不要监听整个body的子树变化,优先选择
#page-manager这类核心容器作为监听目标。
内容的提问来源于stack exchange,提问作者Юра
相关产品推荐
相关产品推荐

