javascript 将变量从内容脚本传递到弹出窗口
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31111721/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Pass a variable from content script to popup
提问by Tetsuo_and_Youth
I'm messing around (trying to learn) how to make a chrome extension. Right now I'm just making super simple one where it counts instances of a certain word on a page. I have this part working.
我在胡闹(试图学习)如何制作 chrome 扩展。现在我只是在制作一个超级简单的方法,它可以计算页面上某个单词的实例。我有这部分工作。
What I want to do is send this information to the pop so I can use it to do some other stuff.
我想要做的是将此信息发送到流行音乐,以便我可以用它来做一些其他的事情。
Here is what I have so far:
这是我到目前为止所拥有的:
manifest.json
清单文件.json
{
"manifest_version": 2,
"name": "WeedKiller",
"description": "Totally serious 0% legit extension",
"version": "0.1",
"background": {
"persistent": false,
"scripts": ["background.js"]
},
"permissions":[
"tabs",
"storage"
],
"browser_action": {
"default_icon": "icon.png",
"default_title": "WeedKiller",
"default_popup": "popup.html"
},
"content_scripts": [
{
"matches": [
"http://*/*",
"https://*/*"
],
"js": [
"content.js"
],
"run_at": "document_end"
}
]
}
content.js
内容.js
var elements = document.getElementsByTagName('*');
var count = 0;
function tokeCounter(){
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
for (var j = 0; j < element.childNodes.length; j++) {
var node = element.childNodes[j];
if (node.nodeType === 3) {
var text = node.nodeValue;
if(text == '420'){
count++;
}
var replacedText = text.replace(/420/, '+1');
if (replacedText !== text) {
element.replaceChild(document.createTextNode(replacedText), node);
}
}
}
}
}
tokeCounter();
So what I want to happen is to send the count
variable to the popup so that I can use it there.
所以我想要发生的是将count
变量发送到弹出窗口,以便我可以在那里使用它。
I have looked around and found that I need to do something with chrome.runtime.sendMessage
.
我环顾四周,发现我需要对chrome.runtime.sendMessage
.
I have it so I add this line to the end of content.js:
我有它,所以我将此行添加到 content.js 的末尾:
chrome.runtime.sendMessage(count);
and then in background.js:
然后在 background.js 中:
chrome.runtime.onMessage.addListener(
function(response, sender, sendResponse){
temp = response;
}
);
I'm sort of stuck here as I'm not sure how to send this information to popup and use it.
我有点卡在这里,因为我不确定如何将此信息发送到弹出窗口并使用它。
回答by Xan
As you have properly noticed, you can't send data directly to the popup when it's closed. So, you're sending data to the background page.
正如您所注意到的,当弹出窗口关闭时,您无法将数据直接发送到它。因此,您正在向后台页面发送数据。
Then, whenyou open the popup, you want the data there. So, what are the options?
然后,当您打开弹出窗口时,您需要那里的数据。那么,有哪些选择呢?
Please note:this answer will give bad advice first, and then improve on it. Since OP is learning, it's important to show the thought process and the roadbumps.
请注意:这个答案会先给出不好的建议,然后再改进。由于 OP 正在学习,因此展示思考过程和障碍很重要。
First solution that comes to mind is the following: ask the background page, using Messaging again. Early warning: this will not work or work poorly
想到的第一个解决方案如下:询问后台页面,再次使用消息传递。早期警告:这将不起作用或效果不佳
First off, establish that there can be different types of messages. Modifying your current messaging code:
首先,确定可以有不同类型的消息。修改您当前的消息代码:
// content.js
chrome.runtime.sendMessage({type: "setCount", count: count});
// background.js
chrome.runtime.onMessage.addListener(
function(message, sender, sendResponse) {
switch(message.type) {
case "setCount":
temp = message.count;
break;
default:
console.error("Unrecognised message: ", message);
}
}
);
And now, you could in theory ask that in the popup:
现在,理论上您可以在弹出窗口中询问:
// popup.js
chrome.runtime.sendMessage({type: "getCount"}, function(count) {
if(typeof count == "undefined") {
// That's kind of bad
} else {
// Use count
}
});
// background.js
chrome.runtime.onMessage.addListener(
function(message, sender, sendResponse) {
switch(message.type) {
case "setCount":
temp = message.count;
break;
case "getCount":
sendResponse(temp);
break;
default:
console.error("Unrecognised message: ", message);
}
}
);
Now, what are the problems with this?
现在,这有什么问题?
What's the lifetime of
temp
? You have explicitly stated"persistent": false
in your manifest. As a result, the background page can be unloaded at any time, wiping state such astemp
.You could fix it with
"persistent": true
, but keep reading.Which tab's count do you expect to see?
temp
will have the last data written to it, which may very well not be the current tab.You could fix it with keeping tabs (see what I did there?)on which tab sent the data, e.g. by using:
// background.js /* ... */ case "setCount": temp[sender.tab.id] = message.count; break; case "getCount": sendResponse(temp[message.id]); break; // popup.js chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { // tabs is a single-element array after this filtering chrome.runtime.sendMessage({type: "getCount", id: tabs[0].id}, function(count) { /* ... */ }); });
It's a lot of work though, isn't it? This solution works fine though for non-tab-specific data, after fixing 1.
寿命是
temp
多少?您已"persistent": false
在清单中明确说明。这样一来,后台页面就可以随时卸载,擦除等状态temp
。你可以用 修复它
"persistent": true
,但继续阅读。您希望看到哪个选项卡的计数?
temp
将写入最后一个数据,这很可能不是当前选项卡。您可以通过保留选项卡来修复它(看看我在那里做了什么?)在哪个选项卡上发送数据,例如使用:
// background.js /* ... */ case "setCount": temp[sender.tab.id] = message.count; break; case "getCount": sendResponse(temp[message.id]); break; // popup.js chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { // tabs is a single-element array after this filtering chrome.runtime.sendMessage({type: "getCount", id: tabs[0].id}, function(count) { /* ... */ }); });
不过工作量很大,不是吗?在修复 1 后,此解决方案适用于非标签特定的数据。
Next improvement to consider: do we need the background page to store the result for us? After all, chrome.storage
is a thing; it's a persistent storage that all extension scripts (including content scripts) can access.
要考虑的下一个改进:我们是否需要后台页面来为我们存储结果?毕竟chrome.storage
是一回事;它是所有扩展脚本(包括内容脚本)都可以访问的持久存储。
This cuts the background (and Messaging) out of the picture:
这从图片中删除了背景(和消息):
// content.js
chrome.storage.local.set({count: count});
// popup.js
chrome.storage.local.get("count", function(data) {
if(typeof data.count == "undefined") {
// That's kind of bad
} else {
// Use data.count
}
});
This looks cleaner, and completely bypasses problem 1 from above, but problem 2 gets trickier. You can't directly set/readsomething like count[id]
in the storage, you'll need to read count
out, modify it and write it back. It can get slow and messy.
这看起来更干净,完全绕过了上面的问题 1,但问题 2 变得更加棘手。你不能直接设置/读取类似count[id]
的存储,你需要读count
出,修改它,并把它写回。它可能会变得缓慢和混乱。
Add to that that content scripts are not really aware of their tab ID; you'll need to message background just to learn it. Ugh. Not pretty. Again, this is a great solution for non-tab-specific data.
此外,内容脚本并不真正了解它们的选项卡 ID;你需要给背景留言才能学习它。啊。不漂亮。同样,这对于非标签特定数据来说是一个很好的解决方案。
Then the next question to ask: why do we even need a central location to store the (tab-specific) result? The content script's lifetime is the page's lifetime. You can ask the content script directly at any point. Including from the popup.
那么下一个要问的问题是:为什么我们甚至需要一个中心位置来存储(特定于标签的)结果?内容脚本的生命周期就是页面的生命周期。您可以随时直接询问内容脚本。包括从弹出窗口。
Wait, wait, didn't you say at the very top you can't send data to the popup?Well, yes, kinda: when you don't know if it's there listening. But if the popup asks, then it must be ready to get a response, no?
等等,等等,你不是在最上面说不能向弹出窗口发送数据吗?嗯,是的,有点:当你不知道它是否在那里听的时候。但是如果弹出窗口询问,那么它必须准备好获得响应,不是吗?
So, let's reverse the content script logic. Instead of immediately sending the data, wait and listen for requests:
所以,让我们颠倒内容脚本逻辑。不是立即发送数据,而是等待并监听请求:
chrome.runtime.onMessage.addListener(
function(message, sender, sendResponse) {
switch(message.type) {
case "getCount":
sendResponse(count);
break;
default:
console.error("Unrecognised message: ", message);
}
}
);
Then, in the popup, we need to query the tabthat contains the content script. It's a different messaging function, and we have to specify the tab ID.
然后,在弹出窗口中,我们需要查询包含内容脚本的选项卡。这是一个不同的消息传递功能,我们必须指定选项卡 ID。
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {type: "getCount"}, function(count) {
/* ... */
});
});
Now that's much cleaner.Problem 2 is solved: we query the tab we want to hear from. Problem 1 seems to be solved: as long as a script counted what we need, it can answer.
现在干净多了。问题 2 解决了:我们查询我们想要听到的选项卡。问题1似乎解决了:只要一个脚本计算我们需要什么,它就可以回答。
Do note, as a final complication, that content scripts are not always injected when you expect them to: they only start to activate on navigation afterthe extension was (re)loaded. Here's an answerexplaining that in great detail. It can be worked around if you wish, but for now just a code path for it:
待办事项,作为最后的并发症,当你期望他们的内容脚本并不总是注射:他们只启动来激活导航后的延长是(重新)加载。这是一个答案,非常详细地解释了这一点。如果你愿意,它可以解决,但现在只是它的代码路径:
function(count) {
if(typeof count == "undefined") {
// That's kind of bad
if(chrome.runtime.lastError) {
// We couldn't talk to the content script, probably it's not there
}
} else {
// Use count
}
}