You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Manifest V3 Chrome扩展与活动标签通信、访问DOM问题求解

问题根源

你当前代码无法正常运行的核心错误如下,全部是语法/逻辑/路径类硬错误,和扩展权限无关:

  • 文件名拼写错误:main2.js里注入脚本写的路径是JS/pageScripts.js,本地实际文件名为pageScript.js(多写了一个s),直接导致脚本注入失败
  • 消息暗号不匹配:发送消息时用的action是backgroundColor,接收端判断的action是colorBackground,两边名称不一致,消息无法触发对应逻辑
  • 变量名引用错误:pageScript.js里打印request,但接收函数的参数名是message,会直接抛出引用错误中断执行
  • 标签查询逻辑错误:自动化窗口是独立popup窗口,chrome.tabs.query查currentWindow:true拿到的是自动化窗口自己的标签,不是新开的YouTube标签,自然拿不到YouTube的DOM权限
  • 固定等待逻辑不可靠:YouTube是单页应用,就算等60秒也可能出现DOM未渲染完成的情况,且固定等待浪费时间
  • HTML结构错误:所有按钮、div等DOM元素全部写在<head>标签里,会导致JS获取元素失败、渲染异常
  • 无效配置:声明的content_scripts没有配置js字段,根本不会自动注入任何脚本,属于无效配置;同时引入了多个不需要的权限,增加安全风险

完整可运行修复方案

先确认你的项目文件结构和下面完全一致,避免路径错误:

扩展根目录/
├─ manifest.json
├─ Main.html
├─ Automation.html
├─ Youtube.png
├─ CSS/
│  └─ style.css
├─ CDN/
│  └─ jquery.min.js
└─ JS/
   ├─ main.js
   ├─ main2.js
   └─ pageScript.js

1. 修复后的manifest.json

删掉无用权限,补充扩展页访问配置:

{
    "name": "Youtube失效视频检测扩展",
    "version": "0.0.0.1",
    "content_security_policy": {
        "extension_pages": "script-src 'self'; object-src 'self'"
    },
    "description": "自动检测YouTube失效视频,无需手动核验",
    "action": {
        "default_popup": "Main.html"
    },
    "web_accessible_resources": [
        {
            "resources": ["Automation.html"],
            "matches": ["<all_urls>"]
        }
    ],
    "host_permissions": ["*://*.youtube.com/*"],
    "permissions":[
        "activeTab",
        "scripting",
        "tabs"
    ],
    "manifest_version": 3,
    "icons": {
        "16": "Youtube.png",
        "48": "Youtube.png",
        "128": "Youtube.png"
    }
}

2. 修复后的Main.html

把DOM元素移到body标签内:

<html>
<head>
    <script type="text/javascript" src="CDN/jquery.min.js"></script>
    <script type="text/javascript" src="JS/main.js"></script>
</head>
<body>
    <button id="AutomationMenu" style="height:100px;width:200px;font-size:20px;">
       打开自动化检测窗口
    </button>
</body>
</html>

3. 修复后的Automation.html

把DOM元素移到body标签内,补充导出功能需要的隐藏文本框:

<html>
<head>
    <script type="text/javascript" src="CDN/jquery.min.js"></script>
    <script type="text/javascript" src="JS/main2.js"></script>
</head>
<body>
    <div id="player"></div>
    <div id="InfoHere" style="height:300px;overflow-y: scroll">
        <span style="font-size:20px">超时倒计时: <span id="TimerText"></span></span>
        <div id="ResultList" style="margin-top:16px"></div>
    </div>
    <div id="SongURL" style="font-size:30px;margin:16px 0"></div>
    <button id="StartAutomation" style="height:100px;width:200px;font-size:20px;">开始检测</button>
    <button id="create" style="height:100px;width:200px;font-size:20px;">导出结果</button>
    <textarea id="textbox" style="display:none"></textarea>
</body>
</html>

4. 修复后的main.js(默认popup逻辑)

修正扩展页路径,打开自动化窗口后自动关闭默认popup:

document.addEventListener("DOMContentLoaded", () => {
  const startBtn = document.getElementById("AutomationMenu");
  startBtn.addEventListener("click", () => {
    chrome.windows.create({
      height: 1000,
      width: 800,
      url: chrome.runtime.getURL("Automation.html"),
      type: "popup",
    });
    window.close();
  });
});

5. 修复后的pageScript.js(注入YouTube页面的检测脚本)

修正变量名、action名,加入视频失效检测逻辑,支持结果回传:

// 避免重复注入
if (window.injected) return;
window.injected = true;

// 监听扩展端消息
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.action === "checkVideoStatus") {
    // 匹配YouTube失效视频的标志性元素
    const unavailableEl = document.querySelector('#player-error-message-renderer .yt-player-error-message-renderer__title, .promo-title');
    const isUnavailable = unavailableEl?.textContent?.trim().includes("Video unavailable") ?? false;
    
    // 测试功能:正常视频改绿色背景,失效视频改红色背景
    document.body.style.backgroundColor = isUnavailable ? "red" : "green";
    
    // 回传检测结果
    sendResponse({
      isUnavailable: isUnavailable,
      videoUrl: location.href
    });
  }
  return true;
});

6. 修复后的main2.js(自动化窗口核心逻辑)

修正标签ID获取、文件名、action名错误,用页面加载监听替代固定60秒等待,保留倒计时作为超时兜底:

let currentTabId = null;
let timer = 60;
let intervalId = null;
let detectResults = [];

document.addEventListener("DOMContentLoaded", () => {
  // 开始检测逻辑
  $("#StartAutomation").click(function () {
    // 这里替换成你要检测的视频ID列表
    const songURLs = ["OXBxUDJtibw", "dQw4w9WgXcQ"];
    detectResults = [];
    $("#ResultList").empty();
    
    const videoId = songURLs[0];
    const videoUrl = `https://www.youtube.com/watch?v=${videoId}`;
    $("#SongURL").text(`正在检测: ${videoUrl}`);
    
    // 新开标签页时直接拿到标签ID,不需要再query查询
    chrome.tabs.create({ url: videoUrl, active: false }, (tab) => {
      currentTabId = tab.id;
      // 启动超时倒计时
      timer = 60;
      $("#TimerText").text(timer.toString());
      clearInterval(intervalId);
      intervalId = setInterval(() => {
        timer--;
        $("#TimerText").text(timer.toString());
        if (timer <= 0) {
          clearInterval(intervalId);
          addResult(videoUrl, false, "检测超时");
          chrome.tabs.remove(currentTabId);
        }
      }, 1000);

      // 监听页面加载完成,不用傻等60秒
      const tabUpdateListener = (updatedTabId, changeInfo) => {
        if (updatedTabId === currentTabId && changeInfo.status === "complete") {
          // 多等2秒等YouTube渲染完动态DOM
          setTimeout(() => {
            runDetect(currentTabId, videoUrl);
            chrome.tabs.onUpdated.removeListener(tabUpdateListener);
          }, 2000);
        }
      };
      chrome.tabs.onUpdated.addListener(tabUpdateListener);
    });
  });

  // 导出结果逻辑
  $("#create").click(function () {
    const resultText = detectResults.map(item => `${item.videoUrl} | 状态:${item.remark} | 检测时间:${item.time}`).join("\n");
    const textFileAsBlob = new Blob([resultText], { type: "text/plain" });
    const downloadLink = document.createElement("a");
    downloadLink.download = "YouTube失效视频检测结果.txt";
    downloadLink.href = URL.createObjectURL(textFileAsBlob);
    downloadLink.click();
    URL.revokeObjectURL(downloadLink.href);
  });
});

// 执行页面检测
function runDetect(tabId, videoUrl) {
  clearInterval(intervalId);
  // 注入脚本,文件名和本地完全一致
  chrome.scripting.executeScript(
    { target: { tabId: tabId }, files: ["JS/pageScript.js"] },
    () => {
      if (chrome.runtime.lastError) {
        addResult(videoUrl, false, `脚本注入失败:${chrome.runtime.lastError.message}`);
        return;
      }
      // 发送检测消息,action名和接收端完全一致
      chrome.tabs.sendMessage(tabId, { action: "checkVideoStatus" }, (response) => {
        if (chrome.runtime.lastError) {
          addResult(videoUrl, false, `通信失败:${chrome.runtime.lastError.message}`);
          return;
        }
        addResult(
          response.videoUrl, 
          response.isUnavailable, 
          response.isUnavailable ? "视频已失效" : "视频正常"
        );
        // 检测完成后关闭标签页
        chrome.tabs.remove(tabId);
      });
    }
  );
}

// 渲染检测结果
function addResult(videoUrl, isUnavailable, remark) {
  const time = new Date().toLocaleString();
  detectResults.push({ videoUrl, isUnavailable, remark, time });
  $("#ResultList").append(`<div style="color:${isUnavailable ? 'red' : 'green'};margin:8px 0">
    ${videoUrl} <br>
    ${remark}
  </div>`);
}

核心原理解释
  • Chrome扩展操作标签页的核心是拿到准确的tabId,chrome.tabs.create的回调函数会直接返回新标签的ID,不需要额外调用query查询,之前查错窗口是拿不到DOM的核心原因
  • 扩展的消息通信是严格匹配机制,action名称、注入脚本文件名必须完全一致,差一个字符都会导致逻辑不触发
  • YouTube作为React写的单页应用,页面触发complete状态后还要等前端框架渲染完DOM才能查到元素,加1-2秒延迟比固定等60秒效率高得多
  • 所有HTML的可见元素必须放在<body>标签内,写在<head>里会被浏览器自动移到body,导致JS绑定事件失败
  • Chrome扩展的独立popup窗口和默认popup不一样,不会随标签切换自动关闭,适合跑长时间自动化任务

内容的提问来源于stack exchange,提问作者NoNameAccount

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 09:39:43