javascript - 将使用sdk/context-menu API创建的菜单项添加到上下文菜单的顶部

标签 javascript firefox-addon firefox-addon-sdk

我正在开发Mozilla附加SDK扩展,该扩展使用sdk/context-menu API提供了右键单击上下文菜单选项,并在特定网站上执行了一些操作。除了将上下文菜单项添加到列表的底部之外,其他所有操作均正常。我希望它成为上下文菜单中的第一项。我搜索了一段时间以了解如何执行此操作,但是没有运气。

这是我用来创建上下文菜单项的代码(已修改以使使用它的站点模糊不清):

var cm = require("sdk/context-menu");
cm.Item({
   label: "AddonName(d)",
   context: [
     cm.URLContext(["*.somesite.com"]),
     cm.SelectorContext("a[href]")
   ],
   contentScript: 'self.on("click", function (node, data) {' +
               '  self.postMessage(node.href);' +
               '});',
   accessKey: "d",
   onMessage: function (src) {
       //code
     }
});

最佳答案

使用Add-on SDK sdk/context-menu API,除了上下文菜单的底部外,无法将项目直接添加到任何地方。没有方便的方法可以使它最终出现在顶部。但是,您可以添加它,然后将其移动到顶部或上下文菜单中的其他位置(如果需要)。但是,附加SDK无法通过任何附加SDK API来实现。

注意:将上下文菜单项移到菜单顶部会更改通常由Firefox加载项添加的上下文菜单项的操作。如果我加载一个加载项,假设其上下文菜单项位于列表的顶部,我将不满意。如果它至少没有提供选项(simple-prefs)来使上下文菜单项位于底部或我选择的位置,那么我很可能会对AMO发表负面评论。将其放在上下文菜单的顶部假定您的加载项将是该上下文菜单中使用最多的选项。这种情况将在很大程度上取决于用户,您的加载项以及用户如何使用您的加载项。另一方面,如果这是我一直使用的选项,那么我很高兴看到它位于顶部,并与其他菜单项分开。

使上下文菜单项出现在顶部的一种方法:

您当前的代码将在上下文菜单中显示一个链接,该链接的底部具有添加的条目AddonName(d)的链接,如下所示:
original look of link context menu after addition on bottom

上下文菜单是每个Firefox窗口都存在的XUL DOM的一部分。看来,附件SDK sdk/context-menu仅允许您将项目添加到页面内容区域内元素的上下文菜单中。因此,在XUL DOM中,受影响的上下文菜单是具有menupopupid="contentAreaContextMenu"
enter image description here

一旦上下文菜单项的menuitem存在,您就可以操纵XUL DOM将其移动为firstChild,这会将其放在上下文菜单的顶部。但是,SDK不会在第一次显示它之前(或者至少直到第一次显示它时)才将上下文菜单项的menuitem插入XUL DOM中。因此,您不能只调用cm.Item()然后立即更改XUL DOM。

更为复杂的是,XUL DOM操作必须在您的主代码中进行,而您只能从内容脚本中得知将要显示上下文菜单(因此menuitem在XUL DOM中)。因此,我们必须侦听context事件并将消息传递给我们的主脚本以启动上下文菜单的XUL DOM操作。

请记住,由let cmItem = cm.Item()返回的对象实际上并不是XUL DOM中对menuitem的引用。这是因为对于每个Firefox窗口,XUL DOM中的menuitem作为XUL DOM中的单独对象存在。因此,我们必须搜索XUL DOM以找到正确的menuitem元素。因为对于每个Firefox窗口,它都是一个单独的XUL DOM,所以我们需要确保它在所有窗口中都发生。在这种情况下,每次在显示上下文菜单时,在内容脚本中侦听context事件都会导致此更改。因此,我们不需要在每个Firefox窗口中进行专门的更改,因为我们每次在显示上下文菜单时都进行更改。

在XUL DOM中搜索正确的menuitem会增加其他复杂性,因为附加SDK不会提供应用于在XUL DOM中创建的idmenuitem属性。因此,我们必须根据label属性的值找到该项目。这意味着您无需更改该属性,也无需跟踪更改,以搜索正确的menuitem。在下面的示例代码中,假定标签不变。

现在,我们需要将内容脚本传递的消息用于指示上下文菜单即将显示的两条消息,并在单击上下文菜单项后发送node.href。为此,现在传递的是一个具有type属性的对象,该属性指示要传递的消息的类型(clickcontext)和单击后的data

注意:附加SDK还会在上下文菜单中在由附加SDK添加的那些菜单项上方添加menuseparator。可能是在添加的每个上下文菜单项之间或每个附加SDK扩展添加的所有上下文菜单项之间创建了分隔符。但是,在简短的测试中,即使对于多个附加SDK扩展,似乎也只添加了一个分隔符。为了使外观看起来更加整洁,您很多人都想在输入后在上下文菜单的顶部添加一个。如果确实添加了一个,则需要确保在禁用附加组件后将其删除。移动此分隔符将假定您是唯一添加到任何上下文菜单的附加SDK扩展。我没有在下面的示例代码中操作此menuseparator

以下代码将使用menuitem创建的单个cm.Item()从上下文菜单中的任意位置移动到上下文菜单的顶部:

let cm = require("sdk/context-menu");
let cmLabel = "AddonName(d)";
cm.Item({
    label: cmLabel,
    context: [ cm.SelectorContext("a[href]") ],
    contentScript: 'self.on("click", function (node, data) {' +
               '  self.postMessage({type:"click", data:node.href});' +
               '});' +
               'self.on("context", function (node) {' +
               '  self.postMessage({type:"context"});' +
               '  return true;' +
               '});',
    accessKey: "d",
    onMessage: function (message) {
        if(message.type === "click") {
            console.log("context menu selected on:" + message.data);
        } else if (message.type === "context") {
            adjustContextMenuOrder(cmLabel);
        }
     }
});

function adjustContextMenuOrder(label) {
    curWindow = require('sdk/window/utils').getMostRecentBrowserWindow();
    let contextMenulist = curWindow.document.getElementById("contentAreaContextMenu");
    //Get a list of elements with a matching label property.
    let itemList = contextMenulist.querySelectorAll('[label="' + label + '"]');
    menuitem = itemList[0]; //Assume the first one found is the one we want.
    if(menuitem === undefined) {
        return; //Did not find the menuitem.
    }
    let parent = menuitem.parentNode;
    parent.insertBefore(menuitem, parent.firstChild); //Move found element to the top.
}


上面的代码生成一个链接的上下文菜单,如下所示:
context menu moved to top

注意:已对问题中的代码进行了一些更改,以测试或验证功能。 context进行了简化以简化测试(取消了仅在某些匹配的URL上显示的限制)。另外,在node.href中使用console.log()数据来验证功能。

关于javascript - 将使用sdk/context-menu API创建的菜单项添加到上下文菜单的顶部,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37770770/

相关文章:

javascript - 使用图像验证输入文本框

javascript - 如何在无需重新启动的 Firefox 扩展中交换数组文件?是不是更新数组元素而不是整个扩展?

javascript - 如何允许插件的内容脚本从当前选项卡访问 Javascript 变量?

javascript - 使用 document.links 将当前页面的 URL 保存在 addon sdk 中

firefox - 如何使用addon SDK调用Firefox printpreview

javascript - JSF 表单 - 提交 > 关注选定的输入

javascript - 我们可以在 react-data-grid 中使某些行不可编辑吗?

javascript - 使用 Ramda 执行子数组的函数式方法

web - 将选项添加到浏览器的上下文菜单(浏览器的扩展)

iframe - 在 firefox 扩展中的注入(inject) iframe 中加载本地 html 文件