问题描述
有没有办法显示上下文菜单操作,仅当用户右键单击以story"开头的类时.
Is there a way to show context menu actions, only when the user right-clicks on classes that start with "story".
例如:如果用户在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();
}
推荐答案
在 这个相关的答案,我解释说不能即时创建上下文菜单项,因为 contextmenu
事件和上下文菜单项出现之间的时间是不足以获得 chrome.contextMenus.create
中间打电话.
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.
另一个答案解释了如何确保上下文菜单条目显示所选文本.这是通过监听 selectionchange
事件来完成的.为了您的目的,我们希望使用具有所需时间的事件.
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.
我将使用 mouseover
和 mouseout
事件.根据鼠标事件,当您使用键盘时,上下文菜单将不起作用,例如通过使用 JavaScript 或 Tab 键聚焦一个元素,然后按下上下文菜单键.我没有在下面的解决方案中实现它.
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.
演示由三个文件组成(加上一个 HTML 页面来测试它).我将所有文件放在一个 zip 文件中,可在 https://robwu.nl/contextmenu-dom.zip.
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.
每个 Chrome 扩展程序都需要此文件才能工作.对于此应用程序,使用了后台页面和内容脚本.此外,还需要 contextMenus
权限.
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
后台页面将代表内容脚本创建上下文菜单.这是通过将事件侦听器绑定到 chrome.runtime.onConnect
来实现的,它提供了一个简单的界面来替换上下文菜单条目.
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
被调用每当通过此端口(来自内容脚本)接收到消息时.除了 onclick
之外的所有属性都是 JSON 可序列化的,因此只有onclick"处理程序需要特殊处理.我使用字符串来标识字典中的预定义函数 (var clickHandlers
).
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
该脚本的第一部分创建了用于创建上下文菜单的简单方法.
在最后 5 行中,上下文菜单条目被定义并绑定到与给定选择器匹配的所有当前和未来元素.就像我在上一节中所说的,参数类型与 createProperties 相同
chrome.contextMenus.create
的参数,除了onclick",它是一个映射到后台页面中的函数的字符串.
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'}
]);
前面的代码假设所有上下文菜单点击都由后台页面处理.如果要改为处理内容脚本中的逻辑,则需要在内容脚本中绑定消息事件.我已经展示了 chrome.tabs.sendMessage
在 background.js
示例中,显示应在何处触发此事件.
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"开头的类时才显示上下文菜单按钮;的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!