【问题标题】:How to access the webpage DOM rather than the extension page DOM?如何访问网页 DOM 而不是扩展页面 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。

【问题讨论】:

    标签: google-chrome google-chrome-extension


    【解决方案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);
        }
        

      【讨论】:

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