如何访问网页 DOM 而不是扩展页面 DOM?

Posted

技术标签:

【中文标题】如何访问网页 DOM 而不是扩展页面 DOM?【英文标题】:How to access the webpage DOM rather than the extension page DOM? 【发布时间】:2011-05-30 18:06:51 【问题描述】:

我正在编写一个 Chrome 扩展程序,并尝试在 popup.html 文件中单击按钮后立即在当前网页上覆盖 <div>

当我从 popup.html 中访问 document.body.insertBefore 方法时,它会覆盖弹出窗口上的 <div>,而不是当前网页。

我是否必须在 background.html 和 popup.html 之间使用消息传递才能访问网页的 DOM?我想在 popup.html 中做所有事情,如果可能的话,我也想使用 jQuery。

【问题讨论】:

【参考方案1】:

浏览器操作弹出窗口或 ManifestV2 后台脚本等扩展页面/脚本有自己的 DOM、documentwindowchrome-extension:// URL(使用扩展的 devtools for that part 来检查它)。 ManifestV3 service worker 没有任何 DOM/文档。

您需要content script 才能访问网页的 DOM 并与选项卡的内容进行交互。内容脚本将作为该页面的一部分在选项卡中执行,而不是作为扩展程序的一部分。

方法 1. 声明式

manifest.json:

"content_scripts": [
  "matches": ["*://*.example.com/*"],
  "js": ["contentScript.js"]
],

它会在页面加载时运行一次。之后,使用messaging,但请注意,它不能发送 DOM 元素、Map、Set、ArrayBuffer、类、函数等 - 它只能发送与 JSON 兼容的简单对象和类型,因此您需要手动提取所需数据并将其作为简单数组或对象传递。

方法2.程序化

ManifestV2:

使用chrome.tabs.executeScript 按需注入内容脚本。

此方法的回调接收内容脚本中最后一个表达式的结果,因此可用于提取必须与 JSON 兼容的数据,请参见上面的方法 1 注释。

manifest.json 中的必需权限:

最佳情况:"activeTab",适用于对用户操作的响应(通常是单击工具栏中的扩展图标)。安装扩展时不显示权限警告。

通常:"*://*.example.com/" 以及您想要的任何其他网站。

最坏的情况:"<all_urls>""*://*/""http://*/""https://*/" - 当提交到 Chrome 网上应用店时,由于广泛的主机权限,所有这些都会将您的扩展程序置于超级缓慢的审核队列中。

ManifestV3与上述的区别:

使用chrome.scripting.executeScript。

manifest.json 中需要 permissions

"scripting" - 强制 "activeTab" - 理想情况,见上面 ManifestV2 的注释。

如果无法实现理想情况,请将允许的站点添加到 manifest.json 中的 host_permissions

【讨论】:

【参考方案2】:

为了说明编程注入,让我们在单击浏览器操作时添加该 div。

ManifestV2

简单调用:

chrome.tabs.executeScript( code: `($ inContent1 )()` );

function inContent1() 
  const el = document.createElement('div');
  el.style.cssText = 'position:fixed; top:0; left:0; right:0; background:red';
  el.textContent = 'DIV';
  document.body.appendChild(el);

带参数调用并接收结果:

chrome.tabs.executeScript(
  code: `($ inContent2 )($ JSON.stringify( foo: 'bar' ) )`
, ([result] = []) => 
  if (!chrome.runtime.lastError) 
    console.log(result); // shown in devtools of the popup window
  
);

function inContent2(params) 
  const el = document.createElement('div');
  el.style.cssText = 'position:fixed; top:0; left:0; right:0; background:red';
  el.textContent = params.foo;
  document.body.appendChild(el);
  return 
    success: true,
    html: document.body.innerHTML,
  ;

这个例子使用了inContent函数代码到字符串的自动转换,这里的好处是IDE可以应用语法高亮和linting。明显的缺点是浏览器会浪费时间来解析代码,但通常不到 1 毫秒,因此可以忽略不计。

ManifestV3

不要忘记 manifest.json 中的权限,有关更多信息,请参阅其他答案。

简单调用:

async function tabAddDiv() 
  const [tab] = await chrome.tabs.query(active: true, currentWindow: true);
  await chrome.scripting.executeScript(
    target: tabId: tab.id,
    func: inContent1, // see inContent1 in ManifestV2 example above
  );

注意:在 Chrome 91 或更早版本中,func: 应为 function:

带参数调用并接收结果

需要 Chrome 92,因为它实现了args。 一个简单的例子:

res = await chrome.scripting.executeScript(
  target: tabId: tab.id,
  func: (a, b) =>  /* use a and b */ ,
  args: ['foo', 'bar'],
);

现在让我们使用上面 ManifestV2 代码中的 inContent2 函数:

async function tabAddDiv() 
  const [tab] = await chrome.tabs.query(active: true, currentWindow: true);
  let res;
  try 
    res = await chrome.scripting.executeScript(
      target: tabId: tab.id,
      func: inContent2,
      args: [ foo: 'bar' ], // arguments must be JSON-serializable
    );
   catch (e) 
    console.warn(e.message || e);
    return;
  
  // res[0] contains results for the main page of the tab 
  document.body.textContent = JSON.stringify(res[0].result);

【讨论】:

此脚本将在后台或内容中进入哪个文件? 这个答案是主要答案的插图。请先阅读。 我已阅读,但您的回答不清楚在哪里 内容脚本是被注入的内容,因此您显然无法在被注入的内容中进行注入。我认为您应该再次阅读主要答案,也许还可以查看文档。

以上是关于如何访问网页 DOM 而不是扩展页面 DOM?的主要内容,如果未能解决你的问题,请参考以下文章

浏览器扩展是否可以读取网页的DOM而无需在选项卡中打开它?

通过 chrome 扩展访问 DOM 元素

JavaScript之dom操作

[DOM]初识DOM

由 Chrome 扩展触发的 DOM 事件是不是在主页的上下文中传播?

DOM