Chrome 扩展如何将数据从内容脚本发送到 popup.html

IT技术 javascript jquery html google-chrome google-chrome-extension
2021-01-19 11:13:33

我知道这在很多帖子中都被问到过,但老实说我不明白。我是 JavaScript、Chrome 扩展和所有东西的新手,我有这门课。所以我需要制作一个插件,使用跨域请求来计算任何给定页面上的 DOM 对象。到目前为止,我已经能够使用 Chrome 扩展 API 实现这一点。现在的问题是我需要在我的 popup.html 页面上从 contentScript.js 文件中显示数据。我不知道该怎么做,我已经尝试阅读文档,但是在 chrome 中发送消息我就是不明白该怎么做。

以下是到目前为止的代码。

清单文件.json

{
"manifest_version":2,

"name":"Dom Reader",
"description":"Counts Dom Objects",
"version":"1.0",

"page_action": {
    "default_icon":"icon.png",
    "default_title":"Dom Reader",
    "default_popup":"popup.html"
},

"background":{
    "scripts":["eventPage.js"],
    "persistent":false
},

"content_scripts":[
    {
        "matches":["http://pluralsight.com/training/Courses/*", "http://pluralsight.com/training/Authors/Details/*",                                          "https://www.youtube.com/user/*", "https://sites.google.com/site/*", "http://127.0.0.1:3667/popup.html"],
        "js":["domReader_cs.js","jquery-1.10.2.js"]
        //"css":["pluralsight_cs.css"]
    }
],

"permissions":[
    "tabs",
    "http://pluralsight.com/*",
    "http://youtube.com/*",
    "https://sites.google.com/*",
    "http://127.0.0.1:3667/*"
]

弹出窗口.html

<!doctype html>
<html>

    <title> Dom Reader </title>    
    <script src="jquery-1.10.2.js" type="text/javascript"></script>
    <script src="popup.js" type="text/javascript"></script>

<body>
    <H1> Dom Reader </H1>
    <input type="submit" id="readDom" value="Read DOM Objects" />

   <div id="domInfo">

    </div>
</body>
</html>

事件页面.js

var value1,value2,value3;

chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
if (request.action == "show") {
    chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
        chrome.pageAction.show(tabs[0].id);
    });
}

value1 = request.tElements;
});

弹出窗口.js

$(function (){
$('#readDom').click(function(){
chrome.tabs.query({active: true, currentWindow: true}, function (tabs){
    chrome.tabs.sendMessage(tabs[0].id, {action: "readDom"});

 });
});
});

内容脚本

var totalElements;
var inputFields;
var buttonElement;

chrome.runtime.onMessage.addListener(function (request, sender, sendResponse){
if(request.action == "readDom"){

    totalElements = $("*").length;
    inputFields = $("input").length;
    buttonElement = $("button").length;


}
})

chrome.runtime.sendMessage({ 
action: "show", 
tElements: totalElements, 
Ifields: inputFields, 
bElements: buttonElement 

});

任何帮助将不胜感激,请避免我所做的任何菜鸟:)

2个回答

尽管您的方向肯定是正确的(实际上已经接近尾声了),但您的代码中有几个(imo)不好的做法(例如,为这样一个微不足道的任务注入整个库(jquery),声明不必要的权限,使多余的调用 API 方法等)。
我没有亲自测试您的代码,但从快速概览来看,我相信更正以下内容可能会产生一个有效的解决方案(尽管不是非常接近最佳):

  1. manifest.json 中:更改内容脚本的顺序,将 jquery 放在首位。根据相关文档

    "js" [...] 要注入匹配页面的 JavaScript 文件列表。它们按照它们在此数组中出现的顺序注入

    (强调我的)

  2. contentscript.js:移动chrome.runtime.sendMessage({...})onMessage听者回调。


也就是说,这是我建议的方法:

控制流:

  1. 内容脚本被注入到符合某些标准的每个页面中。
  2. 一旦注入,内容脚本就会向事件页面(又名非持久性后台页面)发送一条消息,并且事件页面将页面操作附加到选项卡上。
  3. 一旦页面操作弹出窗口被加载,它就会向内容脚本发送一条消息,询问它需要的信息。
  4. 内容脚本处理请求并做出响应,以便页面操作弹出窗口可以显示信息。

目录结构:

          root-directory/
           |_____img
                 |_____icon19.png
                 |_____icon38.png
           |_____manifest.json
           |_____background.js
           |_____content.js
           |_____popup.js
           |_____popup.html

清单.json:

{
  "manifest_version": 2,
  "name": "Test Extension",
  "version": "0.0",
  "offline_enabled": true,

  "background": {
    "persistent": false,
    "scripts": ["background.js"]
  },

  "content_scripts": [{
    "matches": ["*://*.stackoverflow.com/*"],
    "js": ["content.js"],
    "run_at": "document_idle",
    "all_frames": false
  }],

  "page_action": {
    "default_title": "Test Extension",
    //"default_icon": {
    //  "19": "img/icon19.png",
    //  "38": "img/icon38.png"
    //},
    "default_popup": "popup.html"
  }

  // No special permissions required...
  //"permissions": []
}

背景.js:

chrome.runtime.onMessage.addListener((msg, sender) => {
  // First, validate the message's structure.
  if ((msg.from === 'content') && (msg.subject === 'showPageAction')) {
    // Enable the page-action for the requesting tab.
    chrome.pageAction.show(sender.tab.id);
  }
});

内容.js:

// Inform the background page that 
// this tab should have a page-action.
chrome.runtime.sendMessage({
  from: 'content',
  subject: 'showPageAction',
});

// Listen for messages from the popup.
chrome.runtime.onMessage.addListener((msg, sender, response) => {
  // First, validate the message's structure.
  if ((msg.from === 'popup') && (msg.subject === 'DOMInfo')) {
    // Collect the necessary data. 
    // (For your specific requirements `document.querySelectorAll(...)`
    //  should be equivalent to jquery's `$(...)`.)
    var domInfo = {
      total: document.querySelectorAll('*').length,
      inputs: document.querySelectorAll('input').length,
      buttons: document.querySelectorAll('button').length,
    };

    // Directly respond to the sender (popup), 
    // through the specified callback.
    response(domInfo);
  }
});

popup.js:

// Update the relevant fields with the new data.
const setDOMInfo = info => {
  document.getElementById('total').textContent = info.total;
  document.getElementById('inputs').textContent = info.inputs;
  document.getElementById('buttons').textContent = info.buttons;
};

// Once the DOM is ready...
window.addEventListener('DOMContentLoaded', () => {
  // ...query for the active tab...
  chrome.tabs.query({
    active: true,
    currentWindow: true
  }, tabs => {
    // ...and send a request for the DOM info...
    chrome.tabs.sendMessage(
        tabs[0].id,
        {from: 'popup', subject: 'DOMInfo'},
        // ...also specifying a callback to be called 
        //    from the receiving end (content script).
        setDOMInfo);
  });
});

popup.html:

<!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript" src="popup.js"></script>
  </head>
  <body>
    <h3 style="font-weight:bold; text-align:center;">DOM Info</h3>
    <table border="1" cellpadding="3" style="border-collapse:collapse;">
      <tr>
        <td nowrap>Total number of elements:</td>
        <td align="right"><span id="total">N/A</span></td>
      </tr>
      <tr>
        <td nowrap>Number of input elements:</td>
        <td align="right"><span id="inputs">N/A</span></td>
      </tr>
      <tr>
        <td nowrap>Number of button elements:</td>
        <td align="right"><span id="buttons">N/A</span></td>
      </tr>
    </table>
  </body>
</html>
你介意帮我解决一个非常相似的问题吗?<a href=" stackoverflow.com/questions/34467627/...>我在这个当前镜头是这个答案几乎你的代码。但我仍然不能得到它的工作,似乎无法找到关于此事的任何很好的帮助。
2021-03-27 11:13:33
哇 !谢谢兄弟,现在检查你的方法我觉得我用我的代码创建了多少问题。非常感谢,这就像一个魅力。:)
2021-03-28 11:13:33
@hkm:因为这就是您发送和接收消息的方式。这一切都在文档中
2021-04-03 11:13:33
你好,对不起,我有一段时间没有上,只是检查了你的评论。我不能接受任何答案。它说你需要 15 个声望点才能这样做。
2021-04-04 11:13:33
为什么chrome.tabs.sendMessage在 popupjs 和 chrome.runtime.onMessage.addListenercontent.js 中使用 为什么.tabs在 popupjs 和.runtimecontent.js 中使用
2021-04-04 11:13:33

您可以为此使用 localStorage。您可以将任何数据以哈希表格式存储在浏览器内存中,然后随时访问。我不确定我们是否可以从内容脚本中访问localStorage(之前被阻止了),请尝试自己进行。以下是通过后台页面执行此操作的方法(我首先将数据从内容脚本传递到后台页面,然后将其保存在 localStorage 中):

在 contentScript.js 中:

chrome.runtime.sendMessage({
  total_elements: totalElements // or whatever you want to send
});

在 eventPage.js(您的背景页面)中:

chrome.runtime.onMessage.addListener(
    function(request, sender, sendResponse){
       localStorage["total_elements"] = request.total_elements;
    }
);

然后您可以使用 localStorage["total_elements"] 在 popup.js 中访问该变量。

也许您可以直接从现代浏览器的内容脚本访问 localStorage。然后你不需要通过你的后台页面传递数据。

关于 localStorage 的好书:http : //diveintohtml5.info/storage.html

请不要提倡使用已弃用的chrome.extension.onRequest/sendRequest(顺便说一句,在执行之前不会加载非持久性后台页面)。请改用chrome.runtime.*
2021-03-18 11:13:33
@ExpertSystem 请,如果您看到此类过时的信息,请建议/进行编辑。
2021-03-21 11:13:33
@Xan:所以,你是 [Google-Chrome-Extension] 的继任者 :) 我不喜欢编辑人们的帖子(我觉得它太烦人了)。此外,有这么多过时的教程和示例(至少在当时是这样),我发现最好明确说明为什么使用不好.extension.xxx,而不是仅仅展示正确的方式(有些人可能认为这是“同样可以替代”)。我猜这更多是风格问题。保持良好的工作 !
2021-04-03 11:13:33