取决于所选文本的动态扩展上下文菜单

问题描述 投票:17回答:1

我正在尝试根据所选内容在Chrome上下文菜单上创建条目。我在Stackoverflow上发现了几个与此有关的问题,对于所有这些问题,答案都是:使用带有“ mousedown”侦听器的内容脚本,该侦听器将查看当前选择并创建上下文菜单。

我实现了此功能,但它并不总是有效。有时,所有日志消息都说上下文菜单已按我的要求进行了修改,但是出现的上下文菜单并未更新。

基于此,我怀疑这是一个竞争条件:有时chrome在代码完全运行之前开始渲染上下文菜单。

我尝试将eventListener添加到“ contextmenu”和“ mouseup”。后者在用户用鼠标选择文本时触发,因此它将在上下文菜单出现之前(甚至几秒钟)更改很多。即使使用这种技术,我仍然会看到相同的错误!

[这种情况在Chrome 22.0.1229.94(Mac)中经常发生,在Chromium 20.0.1132.47(linux)中偶尔发生,在Windows(Chrome 22.0.1229.94)上尝试运行2分钟并没有发生。

到底发生了什么?我该如何解决?还有其他解决方法吗?


这里是我的代码的简化版本(不是那么简单,因为我保留了日志消息):

manifest.json:

{
  "name": "Test",
  "version": "0.1",
  "permissions": ["contextMenus"],
  "content_scripts": [{
    "matches": ["http://*/*", "https://*/*"],
    "js": ["content_script.js"]
  }],
  "background": {
    "scripts": ["background.js"]
  },
  "manifest_version": 2
}

content_script.js

function loadContextMenu() {
  var selection = window.getSelection().toString().trim();
  chrome.extension.sendMessage({request: 'loadContextMenu', selection: selection}, function (response) {
    console.log('sendMessage callback');
  });
}

document.addEventListener('mousedown', function(event){
  if (event.button == 2) {
    loadContextMenu();
  }
}, true);

background.js

function SelectionType(str) {
  if (str.match("^[0-9]+$"))
    return "number";
  else if (str.match("^[a-z]+$"))
    return "lowercase string";
  else
    return "other";
}

chrome.extension.onMessage.addListener(function(msg, sender, sendResponse) {
  console.log("msg.request = " + msg.request);
  if (msg.request == "loadContextMenu") {
    var type = SelectionType(msg.selection);
    console.log("selection = " + msg.selection + ", type = " + type);
    if (type == "number" || type == "lowercase string") {
      console.log("Creating context menu with title = " + type);
      chrome.contextMenus.removeAll(function() {
        console.log("contextMenus.removeAll callback");
        chrome.contextMenus.create(
            {"title": type,
             "contexts": ["selection"],
             "onclick": function(info, tab) {alert(1);}},
            function() {
                console.log("ContextMenu.create callback! Error? " + chrome.extension.lastError);});
      });
    } else {
      console.log("Removing context menu")
      chrome.contextMenus.removeAll(function() {
          console.log("contextMenus.removeAll callback");
      });
    }
    console.log("handling message 'loadContextMenu' done.");
  }
  sendResponse({});
});
javascript google-chrome-extension contextmenu
1个回答
32
投票

contextMenus API用于定义上下文菜单条目。无需在打开上下文菜单之前立即调用它。因此,不要在contextmenu事件上创建条目,而使用selectionchange事件来连续更新contextmenu条目。

我将显示一个简单的示例,该示例仅在上下文菜单条目中显示所选文本,以显示条目之间的同步良好。

使用此内容脚本:

document.addEventListener('selectionchange', function() {
    var selection = window.getSelection().toString().trim();
    chrome.runtime.sendMessage({
        request: 'updateContextMenu',
        selection: selection
    });
});

在后台,我们将只创建一次contextmenu条目。之后,我们更新上下文菜单项(使用从chrome.contextMenus.create中获得的ID)。当选择为空时,如果需要,我们将删除上下文菜单项。

chrome.contextMenus.create

为了使这个示例简单,我假设只有一个上下文菜单项。如果要支持更多条目,请创建一个数组或哈希来存储ID。

提示

  • Optimization-要减少// ID to manage the context menu entry var cmid; var cm_clickHandler = function(clickData, tab) { alert('Selected ' + clickData.selectionText + ' in ' + tab.url); }; chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) { if (msg.request === 'updateContextMenu') { var type = msg.selection; if (type == '') { // Remove the context menu entry if (cmid != null) { chrome.contextMenus.remove(cmid); cmid = null; // Invalidate entry now to avoid race conditions } // else: No contextmenu ID, so nothing to remove } else { // Add/update context menu entry var options = { title: type, contexts: ['selection'], onclick: cm_clickHandler }; if (cmid != null) { chrome.contextMenus.update(cmid, options); } else { // Create new menu, and remember the ID cmid = chrome.contextMenus.create(options); } } } }); API调用的次数,请缓存参数的相关值。然后,使用简单的chrome.contextMenus比较来检查是否需要创建/更新contextMenu项。
  • Debugging-所有===方法都是异步的。要调试代码,请将回调函数传递给chrome.contextMenus.create.create方法。
© www.soinside.com 2019 - 2024. All rights reserved.