【问题标题】:Trying to communicate from default_script to content_script in chrome extension (JavaScript) not working尝试在 Chrome 扩展(JavaScript)中从 default_script 到 content_script 通信不起作用
【发布时间】:2014-08-31 01:25:41
【问题描述】:

好的,所以我正在通过扩展程序更改网站的配色方案,这是我第一次使用 content_scripts,所以是的,我是一个完整的新手,请随意对待我。

问题是 tabs.connect 它不起作用,我需要标签 ID 还是什么?这是我目前所拥有的:

manifest.json:

{
  "manifest_version": 2,

  "name": "ROBLOX Color Scheme",
  "description": "Edit the color scheme of the roblox bar! Note: Not created by roblox.",
  "version": "1.0",

  "permissions": [
    "<all_urls>",
    "tabs"
  ],
  "browser_action": {
    "default_icon": "Icon.png",
    "default_popup": "Popup.html"
  },
  "content_scripts": [
    {
      "matches": ["http://www.roblox.com/*"],
      "js": ["ContentScript.js"]
    }
  ]
}

Popup.html:

<!DOCTYPE html>
<html>
    <head>
        <p>Choose a color:</p>
        <input type="color" id="Color" value="">
        <button type="button" id="Button">Change Color!</button>
    </head>
    <body>
        <script src="Script.js"></script>
    </body>

</html>

Script.js:

function ChangeColor() {
  var TabId;
    chrome.tabs.query({currentWindow: true, active: true}, function(tabArray) {
      TabId = tabArray[0];
    });
  var port = chrome.tabs.connect(TabId, {name: "ColorShare"});
  port.postMessage({Color: document.getElementById("Color").value});
}

document.getElementById('Color').addEventListener("click", ChangeColor);

ContentScript.js:

var Color;
chrome.runtime.onConnect.addListener(function(port) {
  if (port.name == "ColorShare") then {
    port.onMessage.addListener(function(msg) {
      Color = msg.Color;
    });
  }
});

document.getElementsByClassName("header-2014 clearfix")[0].style.backgroundColor = Color;

感谢所有帮助,感谢您抽出宝贵时间回答我的问题!

编辑:由于我自己和回答者的帮助,现在一些文件已经更改,现在这些文件没有产生错误,但没有任何变化,您可能提供的任何帮助都会很棒!以下是当前代码:

Script.js:

chrome.tabs.query({currentWindow: true, active: true}, function(tabArray) {
    var TabId = tabArray[0].id;
    var port = chrome.tabs.connect(TabId, {name: "ColorShare"});

    function ChangeColor() {
        port.postMessage({Color: document.getElementById("Color").value});
    }
    document.getElementById('Color').addEventListener("click", ChangeColor);
});

ContentScript.js:

chrome.runtime.onConnect.addListener(function(port) {
    if (port.name == "ColorShare") {
        port.onMessage.addListener(function(msg) {
            document.querySelector("header-2014 clearfix").style.backgroundColor = msg.Color;
        });
    }
});

编辑:这个问题已经解决了。我不得不使用完全支持内容脚本的 chrome.storage.sync.set 和 chrome.storage.sync.get!我会尽快发布使用的脚本!

【问题讨论】:

  • chrome.tabs.query 是异步的,因此它可能会尝试连接到null 的选项卡。移动您的端口定义,查询中的postMessageTabId 将被定义。
  • 您能否提供如何做到这一点作为答案?
  • 您需要为此使用端口吗?内置的message passing 非常适合在后台页面(扩展程序的主要部分)和内容脚本之间进行通信。
  • @anders 这就是我正在使用的(见 long-lived)
  • @warspyking 够公平

标签: javascript google-chrome google-chrome-extension communication content-script


【解决方案1】:

我认为您误解了端口的概念。一个端口用于会话中的多个消息。 But the port is destroyed when the session ends (for example, when the tab is closed).无论如何,您不应该在每次点击时重新创建端口。

您的一个 cmets 提到刷新页面,这让我认为您希望此颜色在页面重新加载时保持不变。从用户界面的角度来看,这是有道理的,但您确实应该在一开始就提到这一点。

As I said, ports get destroyed when the tab is closed (or reloaded).如果您希望值保持不变,则需要一种存储机制,例如 chrome.storage(您也可以使用本地存储,但前面的链接给出了几个不这样做的理由)。

manifest.json 只需要"permissions": [ "activeTab", "storage" ],。您可能还需要页面操作而不是浏览器操作(或者两者都不需要,我会谈到)。

ContentScript.js:

var myBgColor = false;

chrome.storage.sync.get("myBgColor",function(items) {
    if ( items.myBgColor ) {
        myBgColor = items.myBgColor;
        document.querySelector(".navbar").style.backgroundColor = myBgColor;
    }
});

chrome.runtime.onMessage.addListener(function(request,sender,sendResponse) {
    if ( request.setColor ) {
        document.querySelector(".navbar").style.backgroundColor = request.setColor;
        chrome.storage.sync.set({"myBgColor":request.setColor});
    } else if ( request.getColor ) {
        sendResponse({"myBgColor":myBgColor});
    }
});

我将参数更改为 querySelector,因为我在索引页面上找不到您要查找的元素。

弹出窗口.html:

<!DOCTYPE html>
<html>
<body>
<p>Choose a color:</p>
<input type="color" id="color" value="">
<button type="button" id="button">Change Color!</button>
<script src="Script.js"></script>
</body>
</html>

我不确定您为什么在页面顶部输入内容。

Script.js(但请将您的文件重命名为比 Script.js 更具描述性的名称):

document.getElementById('button').addEventListener("click",function() {
    chrome.tabs.query({currentWindow: true, active: true},function(tabArray) {
        chrome.tabs.sendMessage(tabArray[0].id,{"setColor":document.getElementById("color").value});
    });
});

chrome.tabs.query({currentWindow: true, active: true}, function(tabArray) {
    chrome.tabs.sendMessage(tabArray[0].id,{"getColor":1},setCurColor);
});

function setCurColor(response) {
    if ( response.myBgColor ) {
        document.getElementById("color").value = response.myBgColor;
    }
}

如果有先前设置的背景颜色,我们希望 ContentScript.js 加载消息 Script.js。不幸的是,Script.js 仅在我们单击操作图标时才存在。所以我们有Script.jsContentScript.js 询问当前颜色(如果已设置)。

正如 Ruwanka Madhushan 所注意到的,您的原始脚本(部分)失败了,因为您假设异步 chrome.tabs.query 将在继续执行下一行代码之前完成。异步 javascript 非常强大,但它让您有责任不假设代码已经完成。您需要使用嵌套函数调用,或者匿名(如 Script.js 的 onclick)或通过命名实用函数(如 setCurColor)。 (还有一些 javascript 库可以帮助处理异步函数,但我不知道。)

一切正常,但有一个小问题:Popup.html closes when it loses focus - 在这种情况下,当您单击颜色选择器输入时。 (非常糟糕的)解决方法是打开弹出窗口并右键单击并选择“检查元素”。这会打开弹出窗口的控制台,并防止在您选择颜色时关闭弹出窗口。另一种选择可能是将颜色选择器嵌入到弹出窗口中的 iframe 中(我不知道这是否可行)。

但由于我们正在讨论您的扩展选项,最好的选择可能是使用options page。这也将为您的 html 提供更多空间。例如,您可能需要考虑删除 localStorage.myBgColor 的按钮,以便恢复默认值。或者其他自定义网站的选项,因为我希望你不会为了改变颜色而费尽心思。而且它会隐藏操作图标,因为您可能会设置选项,然后想忘记现有的扩展。

【讨论】:

  • 感谢您让我走上正轨(以及示例代码),我将查看选项页面链接。看起来我应该重新启动代码,+1 用于解释,最好的答案,因为它实际上有帮助(并不是说我不感谢其他 2 个答案)
  • -1 用于在内容脚本中提倡localStorage,因为在内容脚本中localStorage 与页面的域相关联。您正在使用扩展数据污染第三方网站的存储空间。请改用chrome.storage。其他答案已正确解决了问题中的问题,并且问题末尾的代码应该按预期工作,除非重新加载扩展运行时。请不要提倡localStorage,最好解释一下问题末尾的代码为什么不起作用,然后我将删除-1。
  • (并且chrome.runtime.sendMessage 在后台使用端口(创建端口、发送消息、销毁端口);您建议的方法实际上比使用chrome.runtime.conntect 效率低
  • @RobW localStoragechrome.storage 相比有很多负面影响,但您提供的原因(“使用扩展数据污染第三方网站的存储空间”)不是其中之一。这是因为 Chrome 扩展程序存在于单独的沙盒中。
  • @10basetom 内容脚本不在单独的沙箱中运行。他们与页面共享 DOM,包括localStorage。只有执行环境是隔离的。
【解决方案2】:

好的,您正在尝试通过消息传递来执行此操作。我知道你可能认为长寿是要走的路,但事实并非如此。您必须使用存储系统,尤其是与内容脚本完全兼容的 chrome.storage!

您可以简单地创建一个选项页面,而不是使用浏览器操作,该页面会将颜色保存到 chrome.storage,然后内容脚本可以在其中检索它。选项页面只是一些简单的 HTML,但对于刚入门的人来说,代码可能有点复杂。

你想要的选项页面是这样的......

<!DOCTYPE html>
<html>
<head><title>Choose A ROBLOX Color</title></head>
<body>

<h>Color:</h>
<input type="color" id="Color" value="">

<div id="Status"></div>
<button id="Save">Save</button>

<script src="Options.js"></script>
</body>
</html>

Options.js 通过使用在上述 HTML 页面上选择的选项获取选项,并将其保存到 chrome.storage。它使用 chrome.storage.sync.set 来做到这一点

Options.js 看起来有点像这样;

// Saves options to chrome.storage
function save_options() {
  var color = document.getElementById('Color').value;
  chrome.storage.sync.set({
    Color: color
  }, function() {
    // Update status to let user know options were saved.
    var status = document.getElementById('Status');
    status.textContent = 'Options saved.';
    setTimeout(function() {
      status.textContent = '';
    }, 750);
  });
}

// Restores select box and checkbox state using the preferences
// stored in chrome.storage.
function restore_options() {
  // Use default value color = 'red'
  chrome.storage.sync.get({
    Color: 'red'
  }, function(items) {
    document.getElementById('Color').value = items.Color;
  });
}
document.addEventListener('DOMContentLoaded', restore_options);
document.getElementById('Save').addEventListener('click',
    save_options);

最后,内容脚本使用 chrome.storage.sync.get 检索数据,然后设置站点的栏颜色。

内容脚本如下所示;

chrome.storage.sync.get({
    Color: 'red'
    }, function(items) {
    document.getElementsByClassName("header-2014 clearfix")[0].style.backgroundColor = items.Color;
    }
);

manifest文件应该有“storage”权限,需要包含“options_page”

如果您不确定 manifest.json 的外观,应该是这样的:

{
  "manifest_version": 2,

  "name": "ROBLOX Color Scheme",
  "description": "Edit the color scheme of the roblox bar! Note: Not created by roblox.",
  "version": "1.0",
   "options_page": "Options.html",

  "permissions": [
    "<all_urls>",
    "storage"
  ],
  "content_scripts": [
    {
        "matches": ["http://www.roblox.com/*"],
        "js": ["ContentScript.js"]
    }
  ]
}

【讨论】:

    【解决方案3】:

    未测试,但我认为您应该这样做:

    Script.js:

    chrome.tabs.query({currentWindow: true, active: true}, function(tabArray) {
        var TabId = tabArray[0];
        var port = chrome.tabs.connect(TabId, {name: "ColorShare"});
    
        function ChangeColor() {
            port.postMessage({Color: document.getElementById("Color").value});
        });
        document.getElementById('Color').addEventListener("click", ChangeColor);
    }
    

    ContentScript.js:

    chrome.runtime.onConnect.addListener(function(port) {
        if (port.name == "ColorShare") {
            port.onMessage.addListener(function(msg) {
                document.querySelector("header-2014 clearfix").style.backgroundColor = msg.Color;
            });
        }
    });
    

    您应该使用 chrome.tabs.sendMessage 而不是 chrome.tabs.connect

    【讨论】:

    • chrome.tabs.query 调用丢失 );
    • 哦。我会检查一下。
    • 好吧,那是我的错。我以为我发现了一个错误并将其删除(并编辑了他的帖子)所以现在我遇到了另一个错误要检查:P
    • 好的,我刚刚发现这里确实有错误,);应该在最后一个 } 而不是最后一个 ChangeColor 上。运行之后,我得到了这个错误;响应 tabs.query 时出错:错误:调用表单 tabs.connect(object, object) 与定义不匹配 tabs.connect(integer tabId, optional object connectInfo) Popup.html:1
    • 我认为这意味着 tabArray[0] 返回了一个对象,而不是一个 Id?如果是,我们如何获取Id?
    【解决方案4】:

    Script.js

    function ChangeColor() {
      var tabId;
      chrome.tabs.query({currentWindow: true, active: true}, function(tabArray) {
        tabId = tabArray[0].id;
        chrome.tabs.sendMessage(tabId,{color: document.getElementById("Color").value});
      });
    }
    
    document.getElementById('Color').addEventListener("click", ChangeColor);
    

    ContentScript.js

    chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
      document.getElementsByClassName("header-2014 clearfix")[0].style.backgroundColor = msg.color;
    });
    

    这可以解决您的异步问题以及切换到单个消息而不是长期连接。除非您来回发送大量信息,否则单个消息可能比打开端口更好。您也可以在消息中添加某种描述,以附加字段的形式,例如:

    chrome.tabs.sendMessage(tabId,{type: 'setColor', color: stuff});
    

    然后您可以检查侦听器中的类型并像这样将其分开。

    【讨论】:

    • 我使用的是长期连接,因为每当我访问该站点时,它必须“获取”颜色,并且按钮必须“设置”颜色。有更好的方法吗?
    【解决方案5】:

    我认为出于您的目的不需要长期连接,只需使用simple one time request。顺便说一句,由于您使用的是长期连接,所以我会坚持回答。

    首先,您有一个 id 为 Button 的按钮,但您正在为颜色输入附加点击事件处理程序 document.getElementById('Color').addEventListener("click", ChangeColor); 您必须更改这是我的 popup.html 代码。

    popup.html

    <!DOCTYPE html>
    <html>
        <head>
            <p>Choose a color:</p>
            <input type="color" id="ColorVal">
            <button type="button" id="color">Change Color!</button>
        </head>
        <body>
            <script src="script.js"></script>
        </body>
    
    </html>
    

    在 Script.js 中相应地更改 id。除此之外,您编辑的 Script.js 和 ContentScript.js 都很好。您的第一个代码失败,因为查询选项卡需要很多时间。由于 tabid 不存在,端口声明失败。希望这会对你有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-22
      • 1970-01-01
      • 2012-09-19
      • 1970-01-01
      相关资源
      最近更新 更多