如何确保Chrome扩展脚本每次访问YouTube播放页时都运行?
问题:确保Chrome扩展仅在YouTube播放页稳定运行
我正在编写一款Chrome扩展,希望在访问YouTube播放页(youtube.com/watch?)时触发运行。参考10年前的相关问题,解决方案效果不稳定。
当前manifest.json配置导致content.js在所有YouTube页面运行,而非仅播放页:
"content_scripts": [ { "js": ["scripts/content.js"], "matches": ["https://www.youtube.com/*"] } ]
content.js使用事件监听器,但并非每次加载播放页都运行,有时需刷新才生效。脚本功能是读取视频当前时间、时长和播放速度,计算剩余观看时间并显示在视频信息面板中:
document.addEventListener("yt-navigate-start", removeRemainingTime); document.addEventListener("yt-navigate-finish", setupRemainingTimeDisplay); let intervalId; function setupRemainingTimeDisplay() { const video = document.querySelector("video"); const timeWrapper = document.querySelector(".ytp-time-wrapper"); if (timeWrapper && video) { // Create or find the existing span for remaining time let remainingTime = document.querySelector(".remaining-time"); if (!remainingTime) { remainingTime = document.createElement("span"); remainingTime.className = "remaining-time"; remainingTime.style.color = "#ddd"; timeWrapper.appendChild(remainingTime); } // Update remaining time every second intervalId = setInterval(() => { updateRemainingTime(video, remainingTime); }, 1000); } } function updateRemainingTime(video, remainingTime) { const { duration, playbackRate, currentTime } = video; if (!duration || isNaN(playbackRate) || isNaN(currentTime)) { remainingTime.textContent = " • Invalid time"; return; } const timeLeft = calculateRemainingTime(currentTime, playbackRate, duration); remainingTime.textContent = ` • ${formatTimestamp(timeLeft)} left`; } function calculateRemainingTime(currentTime, playbackRate, duration) { return Math.max((duration - currentTime) / playbackRate, 0); } function formatTimestamp(seconds) { const totalSeconds = Math.floor(seconds); const hours = Math.floor(totalSeconds / 3600); const minutes = Math.floor((totalSeconds % 3600) / 60); const secs = totalSeconds % 60; if (hours > 0) { return `${hours}:${String(minutes).padStart(2, "0")}:${String( secs ).padStart(2, "0")}`; } else { return `${minutes}:${String(secs).padStart(2, "0")}`; } } function removeRemainingTime() { const remainingTimeElements = document.querySelectorAll(".remaining-time"); remainingTimeElements.forEach((element) => element.remove()); clearInterval(intervalId); }
请问如何确保content.js每次访问YouTube播放页时都能运行?
解决方案
1. 精准匹配YouTube播放页URL
修改manifest.json的匹配规则,只针对播放页加载脚本,同时指定脚本执行时机:
"content_scripts": [ { "js": ["scripts/content.js"], "matches": ["https://www.youtube.com/watch*"], "run_at": "document_idle" } ]
watch*确保匹配所有带watch路径的URL(包括带视频ID参数的?v=xxx)run_at: "document_idle"保证DOM结构加载完成后再执行脚本,避免初始阶段找不到目标元素
2. 适配YouTube的SPA路由机制
YouTube是单页应用,页面切换不会刷新整个页面,仅依赖yt-navigate事件稳定性不足,需结合URL变化监听:
在content.js开头添加URL校验逻辑:
// 页面初始加载时检查并初始化 checkAndSetup(); // 监听浏览器前进后退的URL变化 window.addEventListener('popstate', checkAndSetup); // 监听YouTube内部路由切换完成事件 document.addEventListener('yt-navigate-finish', checkAndSetup); function checkAndSetup() { // 验证当前是否为播放页 if (window.location.pathname === '/watch' || window.location.href.includes('watch?')) { removeRemainingTime(); // 先清理之前的实例 setupRemainingTimeDisplay(); } else { removeRemainingTime(); // 非播放页时清理元素和定时器 } }
3. 优化元素查找的稳定性
YouTube的DOM元素可能动态加载,直接使用querySelector可能找不到目标元素,改用MutationObserver监听元素出现:
修改setupRemainingTimeDisplay函数:
function setupRemainingTimeDisplay() { // 监听目标元素的动态生成 const observer = new MutationObserver((mutations) => { const timeWrapper = document.querySelector(".ytp-time-wrapper"); const video = document.querySelector("video"); if (timeWrapper && video) { observer.disconnect(); // 找到元素后停止监听 initRemainingTime(video, timeWrapper); } }); observer.observe(document.body, { childList: true, subtree: true }); } function initRemainingTime(video, timeWrapper) { let remainingTime = document.querySelector(".remaining-time"); if (!remainingTime) { remainingTime = document.createElement("span"); remainingTime.className = "remaining-time"; remainingTime.style.color = "#ddd"; timeWrapper.appendChild(remainingTime); } updateRemainingTime(video, remainingTime); // 立即更新一次 intervalId = setInterval(() => { updateRemainingTime(video, remainingTime); }, 1000); }
4. 增强清理逻辑的健壮性
确保定时器和DOM元素在页面切换时完全清理,避免内存泄漏:
function removeRemainingTime() { const remainingTimeElements = document.querySelectorAll(".remaining-time"); remainingTimeElements.forEach((element) => element.remove()); if (intervalId) { clearInterval(intervalId); intervalId = null; // 重置定时器ID,避免重复清理报错 } }
内容的提问来源于stack exchange,提问作者Natalie
相关产品推荐
相关产品推荐

