仅当右键单击以“Story”开头的课程时才显示上下文菜单按钮。 [英] Showing context menu buttons only when right-clicked on classes that start with "Story"

查看:166
本文介绍了仅当右键单击以“Story”开头的课程时才显示上下文菜单按钮。的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否有方法显示上下文菜单操作,只有当用户右键点击以story开头的类时。

例如:如果用户右键单击类story ....页面中的对象,上下文菜单按钮应该出现,否则不会发生任何事情。



这是我的代码(尽管它不起作用):

pre $ var divs = document.querySelectorAll([class ^ = story]); //获取以Story开头的所有类

window.oncontextmenu = function(){

for(var i = 0; i< divs.length; i ++ )
{
divs [i] .onclick = function(){
chrome.contextMenus.create

{id:butto1,title :1,contexts:[all],onclick:genericOnClick}
);
chrome.contextMenus.create

{id:button2,title:2,contexts:[all],onclick:genericOnClick }
);
chrome.contextMenus.create

{id:button3,title:3,contexts:[all],onclick:genericOnClick }
);

};
}

返回true;
};


函数genericOnClick(info,tab){
//在这里做一些废话
chrome.contextMenus.removeAll();


解决方案

这个相关的答案,我解释说无法创建上下文菜单项因为在 contextmenu 事件与上下文菜单项的外观之间的时间不足以获得 chrome.contextMenus.create 调用。



另一个答案解释了如何确保上下文菜单项显示选定的文本。这是通过监听 selectionchange 事件完成的。为了您的目的,我们希望使用具有所需时间的事件。



我将使用 mouseover mouseout 事件。取决于鼠标事件,当你使用键盘时,上下文菜单不起作用,例如,通过使用JavaScript或Tab键关注元素,然后按下上下文菜单键。我没有在下面的解决方案中实现它。



演示包含三个文件(加上一个HTML页面来测试它)。我将所有文件放在一个zip文件中,可在 https://robwu.nl/contextmenu-dom.zip



manifest.json



每个Chrome扩展程序都需要这个文件才能工作。对于此应用程序,使用后台页面和内容脚本。此外,还需要 contextMenus 权限。

  {
name:Contextmenu based on activated element,
description:Demo for https://stackoverflow.com/q/14829677,
version:1,
manifest_version:2,
background:{
scripts:[background.js]
},
content_scripts:[{
run_at:document_idle,
js:[contentscript.js],
matches:[< all_urls>]]
}],
permissions:[
contextMenus
]
}



background.js



背景页面将代表内容脚本创建上下文菜单。这是通过将事件侦听器绑定到 chrome.runtime.onConnect ,它提供了一个简单的界面来替换上下文菜单条目。

extensions / contextMenus#method-createrel =nofollow noreferrer> chrome.contextMenus.create (来自内容脚本)。除 onclick 之外的所有属性都是JSON序列化的,所以只有onclick处理程序需要特殊处理。我使用了一个字符串来标识字典中的预定义函数( var clickHandlers )。

  var lastTabId; 
//如果需要,移除给定选项卡的上下文菜单
function removeContextMenus(tabId){
if(lastTabId === tabId)chrome.contextMenus.removeAll();
}
// chrome.contextMenus onclick处理程序:
var clickHandlers = {$ b $'示例':function(info,tab){
//此事件处理程序接收两个参数,定义在
// https://developer.chrome.com/extensions/contextMenus#property-onClicked-callback

//示例:通知选项卡的内容脚本
// chrome.tabs.sendMessage(tab.id,...一些JSON序列化的数据...);

//示例:移除上下文的上下文菜单
removeContextMenus(tab.id);
}
};

chrome.runtime.onConnect.addListener(function(port){
if(!port.sender.tab || port.name!='contextMenus'){
/ /意外/未知的端口,不要干涉它
return;
}
var tabId = port.sender.tab.id;
port.onDisconnect.addListener(function() {
removeContextMenus(tabId);
});
//每次发布消息时,期望它与输入
// chrome.contextMenus.create的createProperties相同,除onclick。
//onclick应该是一个映射到预定义函数的字符串
port.onMessage.addListener(function(newEntries){
chrome.contextMenus.removeAll(function() {
for(var i = 0; i< newEntries.length; i ++){
var createProperties = newEntries [i];
createProperties.onclick = clickHandlers [createProperties.onclick];
chrome.contextMenus.create(createProper关系);
}
});
});
});

//当一个标签被移除时,检查它是否添加了任何上下文菜单条目。如果是这样,请将其删除
chrome.tabs.onRemoved.addListener(removeContextMenus);



contentscript.js



此脚本的第一部分创建用于创建上下文菜单的简单方法。

在最后5行中,将定义上下文菜单条目并绑定到所有当前和未来匹配给定选择器的元素。就像我在前一节中所说的那样,参数类型与 createProperties chrome.contextMenus.create 的参数,除了onclick,它是一个映射到函数的字符串

  //端口管理
var _port;
var getPort = function(){
if(_port)return _port;
_port = chrome.runtime.connect({name:'contextMenus'});
_port.onDisconnect.addListener(function(){
_port = null;
});
return _port;
}

// listOfCreateProperties是一个createProperties数组,它定义在
// https://developer.chrome.com/extensions/contextMenus#method-create
//只有一个例外:onclick是一个字符串,它对应于
//在后台页面的函数。 (函数不是JSON序列化的,因此这种方法)
函数addContextMenuTo(selector,listOfCreateProperties){
//用于匹配元素的选择器。如果某个元素或其子元素被占用,匹配
selector = selector +','+ selector +'*';
var matches;
['matches','webkitMatchesSelector','webkitMatches','matchesSelector']。
返回true;
}
});
//绑定一个mouseover + mouseout事件以捕捉所有当前和未来元素的悬停。
var isHovering = false;
document.addEventListener('mouseover',function(event){
if(event.target&& event.target [matches](selector)){
getPort()。postMessage (listOfCreateProperties);
isHovering = true;
} else if(isHovering){
getPort()。postMessage([]);
isHovering = false;
}
});
document.addEventListener('mouseout',function(event){
if(isHovering&&(!event.target ||!event.target [matches](selector))){
getPort()。postMessage([]);
isHovering = false;
}
});
}

//示例:将上下文菜单绑定到包含class属性的元素以story开头
addContextMenuTo('[class ^ = story]',[
{id:butto1,title:1,contexts:[all],onclick:'example'},
{id: button2,title:2,contexts:[all],onclick:'example'},
{id:button3,title:3 ,contexts:[all],onclick:'example'}
]);

前面的代码假定所有的上下文菜单点击均由背景页面处理。如果您想要处理内容脚本中的逻辑,则需要在内容脚本中绑定消息事件。我展示了 chrome.tabs(已评论)的实例。 sendMessage background.js 示例中显示该事件的触发位置。



如果您需要确定触发事件的元素,请不要使用预定义函数(在字典中),如我的示例所示,而是使用内联函数或工厂函数。要标识元素,需要将消息与唯一标识符配对。我将把这个实现的任务留给读者(这并不困难)。


Is there a way to show context menu actions, only when the user right-clicks on classes that start with "story".

For example: if the user right-clicks on an object in the page of class "story ....", the context menu buttons should appear, otherwise nothing should happen.

Here is my code (though it does not work):

var divs = document.querySelectorAll("[class^=story]"); //get all classes that start with "Story"

    window.oncontextmenu = function() {

        for(var i=0; i < divs.length; i++)
        {
            divs[i].onclick = function() { 
            chrome.contextMenus.create
            (
                {"id": "butto1", "title": "1", "contexts":["all"], "onclick": genericOnClick}
            );
            chrome.contextMenus.create
            (
                {"id": "button2", "title": "2", "contexts":["all"], "onclick": genericOnClick}
            );
            chrome.contextMenus.create
            (
                {"id": "button3", "title": "3", "contexts":["all"], "onclick": genericOnClick}
            );

            };
        }

        return true; 
    };


function genericOnClick(info, tab) {
  //do some crap here
  chrome.contextMenus.removeAll();
}

解决方案

In this related answer, I explained that context menu items cannot be created on the fly, because the time between a contextmenu event and the appearance of the context menu item is not sufficient to get a chrome.contextMenus.create call in between.

The other answer explains how to make sure that the context menu entry shows the selected text. This was done by listening to the selectionchange event. For your purpose, we want to use an event which has the desired timing.

I'm going to use the mouseover and mouseout events. By depending on mouse events, the context menu will not work when you use the keyboard, e.g. by focusing an element using JavaScript or the Tab key, followed by pressing the context menu key. I did not implement it in the solution below.

The demo consists of three files (plus an HTML page to test it). I put all files in a zip file, available at https://robwu.nl/contextmenu-dom.zip.

manifest.json

Every Chrome extension requires this file in order to work. For this application, a background page and content script is used. In addition, the contextMenus permission is required.

{
    "name": "Contextmenu based on activated element",
    "description": "Demo for https://stackoverflow.com/q/14829677",
    "version": "1",
    "manifest_version": 2,
    "background": {
        "scripts": ["background.js"]
    },
    "content_scripts": [{
        "run_at": "document_idle",
        "js": ["contentscript.js"],
        "matches": ["<all_urls>"]
    }],
    "permissions": [
        "contextMenus"
    ]
}

background.js

The background page will create the context menus on behalf of the content script. This is achieved by binding an event listener to chrome.runtime.onConnect, which offers a simple interface to replace context menu entries.

chrome.contextMenus.create is called whenever a message is received over this port (from the content script). All properties, except for onclick are JSON-serializable, so only the "onclick" handler needs a special treatment. I've used a string to identify a pre-defined function in a dictionary (var clickHandlers).

var lastTabId;
// Remove context menus for a given tab, if needed
function removeContextMenus(tabId) {
    if (lastTabId === tabId) chrome.contextMenus.removeAll();
}
// chrome.contextMenus onclick handlers:
var clickHandlers = {
    'example': function(info, tab) {
        // This event handler receives two arguments, as defined at
        // https://developer.chrome.com/extensions/contextMenus#property-onClicked-callback

        // Example: Notify the tab's content script of something
        // chrome.tabs.sendMessage(tab.id, ...some JSON-serializable data... );

        // Example: Remove contextmenus for context
        removeContextMenus(tab.id);
    }
};

chrome.runtime.onConnect.addListener(function(port) {
    if (!port.sender.tab || port.name != 'contextMenus') {
        // Unexpected / unknown port, do not interfere with it
        return;
    }
    var tabId = port.sender.tab.id;
    port.onDisconnect.addListener(function() {
        removeContextMenus(tabId);
    });
    // Whenever a message is posted, expect that it's identical to type
    // createProperties of chrome.contextMenus.create, except for onclick.
    // "onclick" should be a string which maps to a predefined function
    port.onMessage.addListener(function(newEntries) {
        chrome.contextMenus.removeAll(function() {
            for (var i=0; i<newEntries.length; i++) {
                var createProperties = newEntries[i];
                createProperties.onclick = clickHandlers[createProperties.onclick];
                chrome.contextMenus.create(createProperties);
            }
        });
    });
});

// When a tab is removed, check if it added any context menu entries. If so, remove it
chrome.tabs.onRemoved.addListener(removeContextMenus);

contentscript.js

The first part of this script creates simple methods for creating context menus.
In the last 5 lines, the context menu entries are defined and bound to all current and future elements which match the given selector. Like I said in the previous section, the argument type is identical to the createProperties argument of chrome.contextMenus.create except for "onclick", which is a string which maps to a function in the background page.

// Port management
var _port;
var getPort = function() {
    if (_port) return _port;
    _port = chrome.runtime.connect({name: 'contextMenus'});
    _port.onDisconnect.addListener(function() {
        _port = null;
    });
    return _port;   
}

// listOfCreateProperties is an array of createProperties, which is defined at
// https://developer.chrome.com/extensions/contextMenus#method-create
// with a single exception: "onclick" is a string which corresponds to a function
// at the background page. (Functions are not JSON-serializable, hence this approach)
function addContextMenuTo(selector, listOfCreateProperties) {
    // Selector used to match an element. Match if an element, or its child is hovered
    selector = selector + ', ' + selector + ' *';
    var matches;
    ['matches', 'webkitMatchesSelector', 'webkitMatches', 'matchesSelector'].some(function(m) {
        if (m in document.documentElement) {
            matches = m;
            return true;
        }
    });
    // Bind a single mouseover+mouseout event to catch hovers over all current and future elements.
    var isHovering = false;
    document.addEventListener('mouseover', function(event) {
        if (event.target && event.target[matches](selector)) {
            getPort().postMessage(listOfCreateProperties);
            isHovering = true;
        } else if(isHovering) {
            getPort().postMessage([]);
            isHovering = false;
        }
    });
    document.addEventListener('mouseout', function(event) {
        if (isHovering && (!event.target || !event.target[matches](selector))) {
            getPort().postMessage([]);
            isHovering = false;
        }
    });
}

// Example: Bind the context menus to the elements which contain a class attribute starts with "story"
addContextMenuTo('[class^=story]', [
    {"id": "butto1", "title": "1", "contexts":["all"], "onclick": 'example'},
    {"id": "button2", "title": "2", "contexts":["all"], "onclick": 'example'},
    {"id": "button3", "title": "3", "contexts":["all"], "onclick": 'example'}
]);

The previous code assumes that all context menu clicks are handled by the background page. If you want to handle the logic in the content script instead, you need to bind message events in the content script. I've shown an (commented) instance of chrome.tabs.sendMessage in the background.js example, to show where this event should be triggered.

If you need to identify which element triggered the event, don't use a predefined function (in a dictionary) as shown in my example, but an inline function or a factory function. To identify the element, a message needs to be paired with an unique identifier. I'll leave the task of creating this implementation to the reader (it's not difficult).

这篇关于仅当右键单击以“Story”开头的课程时才显示上下文菜单按钮。的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆