草庐IT

javascript - 将变量从内容脚本传递到弹出窗口

coder 2024-05-14 原文

我正在研究(尝试学习)如何制作 chrome 扩展程序。现在我只是做一个 super 简单的,它计算页面上某个单词的实例。我有这部分工作。

我想做的是将此信息发送给 pop,这样我就可以用它来做一些其他事情。

这是我目前所拥有的:

list .json

{
    "manifest_version": 2,
    "name": "WeedKiller",
    "description": "Totally serious $100% 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"
        }
    ]
}

内容.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();

所以我想要发生的是将 count 变量发送到弹出窗口,以便我可以在那里使用它。

我环顾四周,发现我需要用 chrome.runtime.sendMessage 做点什么。

我有它,所以我将这一行添加到 content.js 的末尾:

 chrome.runtime.sendMessage(count);

然后在 background.js 中:

chrome.runtime.onMessage.addListener(
    function(response, sender, sendResponse){      
       temp = response;
    }
);

我有点卡在这里,因为我不确定如何将此信息发送到弹出窗口并使用它。

最佳答案

您已经注意到,当弹出窗口关闭时,您无法将数据直接发送到该弹出窗口。因此,您正在向后台页面发送数据。

然后,您打开弹出窗口时,您需要那里的数据。那么,有哪些选择呢?

请注意:这个回答会先给出不好的建议,然后再改进。由于 OP 正在学习,因此展示思维过程和路障很重要。


想到的第一个解决方案如下:询问后台页面,再次使用 Messaging。 预警:这将不起作用或效果不佳

首先,确定可以有不同类型的消息。修改您当前的消息代码:

// 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);
        }
    }
);

现在,理论上您可以在弹出窗口中询问:

// 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);
        }
    }
);

现在,这有什么问题?

  1. temp 的生命周期是多少?您已明确说明 "persistent": false in your manifest .因此,后台页面可以随时卸载,删除 temp 等状态。

    您可以使用 "persistent": true 修复它,但请继续阅读。

  2. 您希望看到哪个选项卡的计数? 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 后,此解决方案适用于非特定于选项卡的数据。


下一步要考虑的改进:我们是否需要后台页面来为我们存储结果?毕竟,chrome.storage is a thing ;它是所有扩展脚本(包括内容脚本)都可以访问的持久存储。

这会从图片中删除背景(和消息):

// 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
    }
});

这看起来更清晰,并且完全绕过了上面的问题 1,但问题 2 变得更加棘手。 You can't directly set/read像存储中的count[id],你需要读出count,修改它并写回。它可能会变得缓慢而困惑。

此外,内容脚本并不真正知道它们的标签 ID;您需要向背景发送消息才能了解它。啊。不漂亮。 同样,对于非特定于选项卡的数据,这是一个很好的解决方案。


那么下一个问题要问:为什么我们甚至需要一个中心位置来存储(特定于选项卡的)结果?内容脚本的生命周期就是页面的生命周期。您可以随时直接询问内容脚本。包括来自弹出窗口。

等等,您不是在最上面说过不能向弹出窗口发送数据吗? 嗯,是的,有点:当您不这样做时不知道它是否在那里听。但如果弹出窗口询问,那么它必须准备好获得响应,不是吗?

那么,让我们反转内容脚本逻辑。不要立即发送数据,而是等待并监听请求:

chrome.runtime.onMessage.addListener(
    function(message, sender, sendResponse) {
        switch(message.type) {
            case "getCount":
                sendResponse(count);
                break;
            default:
                console.error("Unrecognised message: ", message);
        }
    }
);

然后,在弹出窗口中,我们需要查询包含内容脚本的选项卡。这是一个不同的消息功能,我们必须指定选项卡 ID。

    chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
        chrome.tabs.sendMessage(tabs[0].id, {type: "getCount"}, function(count) {
            /* ... */
        });
    });

现在更清晰了。问题 2 已解决:我们查询我们想要收听的选项卡。问题1好像解决了:一个脚本只要统计我们需要的就可以回答。

请注意,作为最后一个复杂因素,内容脚本并不总是在您期望的时候注入(inject):它们只会在扩展被(重新)加载后 开始在导航上激活。这是 an answer非常详细地解释了这一点。如果您愿意,可以解决它,但现在只是一个代码路径:

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
    }
}

关于javascript - 将变量从内容脚本传递到弹出窗口,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31111721/

有关javascript - 将变量从内容脚本传递到弹出窗口的更多相关文章

  1. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  2. ruby - 多次弹出/移动 ruby​​ 数组 - 2

    我的代码目前看起来像这样numbers=[1,2,3,4,5]defpop_threepop=[]3.times{pop有没有办法在一行中完成pop_three方法中的内容?我基本上想做类似numbers.slice(0,3)的事情,但要删除切片中的数组项。嗯...嗯,我想我刚刚意识到我可以试试slice! 最佳答案 是numbers.pop(3)或者numbers.shift(3)如果你想要另一边。 关于ruby-多次弹出/移动ruby​​数组,我们在StackOverflow上找到一

  3. ruby - 将数组的内容转换为 int - 2

    我需要读入一个包含数字列表的文件。此代码读取文件并将其放入二维数组中。现在我需要获取数组中所有数字的平均值,但我需要将数组的内容更改为int。有什么想法可以将to_i方法放在哪里吗?ClassTerraindefinitializefile_name@input=IO.readlines(file_name)#readinfile@size=@input[0].to_i@land=[@size]x=1whilex 最佳答案 只需将数组映射为整数:@land边注如果你想得到一条线的平均值,你可以这样做:values=@input[x]

  4. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

    我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

  5. ruby - 通过 ruby​​ 进程共享变量 - 2

    我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是

  6. ruby-on-rails - 独立 ruby​​ 脚本的配置文件 - 2

    我有一个在Linux服务器上运行的ruby​​脚本。它不使用rails或任何东西。它基本上是一个命令行ruby​​脚本,可以像这样传递参数:./ruby_script.rbarg1arg2如何将参数抽象到配置文件(例如yaml文件或其他文件)中?您能否举例说明如何做到这一点?提前谢谢你。 最佳答案 首先,您可以运行一个写入YAML配置文件的独立脚本:require"yaml"File.write("path_to_yaml_file",[arg1,arg2].to_yaml)然后,在您的应用中阅读它:require"yaml"arg

  7. ruby-on-rails - 如何在我的 Rails 应用程序 View 中打印 ruby​​ 变量的内容? - 2

    我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby​​中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R

  8. ruby - 查找字符串中的内容类型(数字、日期、时间、字符串等) - 2

    我正在尝试解析一个CSV文件并使用SQL命令自动为其创建一个表。CSV中的第一行给出了列标题。但我需要推断每个列的类型。Ruby中是否有任何函数可以找到每个字段中内容的类型。例如,CSV行:"12012","Test","1233.22","12:21:22","10/10/2009"应该产生像这样的类型['integer','string','float','time','date']谢谢! 最佳答案 require'time'defto_something(str)if(num=Integer(str)rescueFloat(s

  9. ruby - rails 3 redirect_to 将参数传递给命名路由 - 2

    我没有找到太多关于如何执行此操作的信息,尽管有很多关于如何使用像这样的redirect_to将参数传递给重定向的建议:action=>'something',:controller=>'something'在我的应用程序中,我在路由文件中有以下内容match'profile'=>'User#show'我的表演Action是这样的defshow@user=User.find(params[:user])@title=@user.first_nameend重定向发生在同一个用户Controller中,就像这样defregister@title="Registration"@user=Use

  10. ruby-on-rails - 如何生成传递一些自定义参数的 `link_to` URL? - 2

    我正在使用RubyonRails3.0.9,我想生成一个传递一些自定义参数的link_toURL。也就是说,有一个articles_path(www.my_web_site_name.com/articles)我想生成如下内容:link_to'Samplelinktitle',...#HereIshouldimplementthecode#=>'http://www.my_web_site_name.com/articles?param1=value1¶m2=value2&...我如何编写link_to语句“alàRubyonRailsWay”以实现该目的?如果我想通过传递一些

随机推荐