Context Menu is the menu that appears when a user right clicks (alternative clicks) on an element in a webpage.
In this article we will cover the chrome.contextMenus
API. https://developer.chrome.com/docs/extensions/develop/ui/context-menu. An API responsible for context menus for chrome extensions.
There are 4 major functions normally used in this API chrome.contextMenus.create
, chrome.contextMenus.update
chrome.contextMenus.removeAll
and chrome.contextMenus.onClicked.addListener
. 3 of which, as you can see handle displaying the contextMenu and the latter is an event handle that runs when one of the context menu options has been selected.
Declaring Context Menus in Manifest.json
Well first things first, for one to use the chrome.contextMenus
API they have to declare it first in the manifest.json
file
{
...
"permissions": [
"contextMenus"
],
...
}
Menu Item Types
There are 4 different type of menu item types normal
, checkbox
, radio
and separator
. If you’ve coded HTML before these are similar to the input types. Below is an image showing the different types of context menu item types.
You’ll notice that you can not see the separator
type in the image. That is because it is an empty menu type used to separate checkbox groups and radio groups but more on that later.
In code you define a type either in the chrome.contextMenus.create
or chrome.contextMenus.update
functions.
chrome.contextMenus.create({
id: 'my-checkbox',
title: 'Checkbox',
"type": "checkbox",
contexts: ['page'],
});
selectable text
Context Types
What context types refers to on which element(s) should the context menu item appear on. For instance,
You might what a context menu that only appears when a user selects text
Or one that appears when a user right clicks on an input field.
With that being said let look at the major context types. image
, video
, link
, page
and audio
I believe are self explanatory, then trigger the context menu item to show up when a user right clicks on any of those elements on webpage.
Another important one is selection
this is for a user you have selected text and then have right clicked on it.
editable
is for users that have right clicked into an input field
action
is for the context menu for when a user right clicks on the extension popup icon in the toolbar.
Creating Context Menu in Code
In most cases you want to create a the context menu options when the extension has just been installed. You’d write this in your background scripts
chrome.runtime.onInstalled.addListener((details) => {
chrome.contextMenus.create({
id: 'insert',
title: 'Insert Secret Word',
type: 'normal',
contexts: ['editable'],
});
chrome.contextMenus.create({
id: 'media',
title: 'Get URL',
type: 'normal',
contexts: ['image', 'video'],
});
});
Keep in mind that chrome.contextMenus.create
and chrome.contextMenus.update
functions return a promise or in other words the functions run asynchronously. So sometimes you might want for a menu to be created first before creating the next one.
chrome.runtime.onInstalled.addListener(async (details) => {
await chrome.contextMenus.create({
id: 'insert',
title: 'Insert Secret Word',
type: 'normal',
contexts: ['editable'],
});
await chrome.contextMenus.create({
id: 'media',
title: 'Get URL',
type: 'normal',
contexts: ['image', 'video'],
});
});
Nesting Menus
In certain instances it is a good a idea to nest your context menu to keep them organize for the user.
The way we go this is by add parentId
to the menu item we are creating. So let’s say you want to create menu with two children menu items you’d set the parentId
of the two child elements equal to the id
of the parent. That sounded confusing so it is best to just show you. Here is a code snippet below.
Parent items must have type
“normal”
chrome.contextMenus.create({
id: 'parent',
title: 'Parent',
type: "normal",
contexts: ['action'],
});
chrome.contextMenus.create({
id: 'child1',
parentId: "parent",
title: 'Child 1',
type: "checkbox",
contexts: ['action'],
});
chrome.contextMenus.create({
id: 'child2',
parentId: "parent",
title: 'Child 2',
type: "checkbox",
contexts: ['action'],
});
Also there is a limit to how many parent items (top level menu items) you can create. As of the writing on this article it should be 6. You can find out my running this code in your background scripts and checking your console out
console.log(chrome.contextMenus.ACTION_MENU_TOP_LEVEL_LIMIT)
Page Specific Menu Items
If you want to context menu items to only show up on specific pages you’ll need to specify the a URL patterns in the documentUrlPatterns
for chrome.contextMenus.create
or chrome.contextMenus.update
functions.
Here is a code snippet to create context menu items that will only show either on Google or Facebook.
chrome.contextMenus.create({
id: 'google',
title: 'Only Google',
type: 'normal',
documentUrlPatterns: ["https://*.google.com/", "https://*.google.com/*"],
contexts: ['page'],
});
chrome.contextMenus.create({
id: 'fb',
title: 'Only Facebook',
type: 'normal',
documentUrlPatterns: ["https://www.facebook.com/", "https://www.facebook.com/*"],
contexts: ['page'],
});
Menu Button Groups
By arranging multiple radio
menu items together you’ll create a what we call a button group.
await chrome.contextMenus.create({
id: 'menu-types',
title: 'Menu Types',
type: 'normal',
contexts: ['page'],
});
await chrome.contextMenus.create({
id: 'radio1',
parentId: "menu-types",
title: 'Radio 1',
type: "radio",
contexts: ['page'],
});
await chrome.contextMenus.create({
id: 'radio2',
parentId: "menu-types",
title: 'Radio 2',
type: "radio",
contexts: ['page'],
});
Radio Menu behave the same way <input type="radio" />
in HTML do. If arranged in one after the other, only one gets selected when clicked.
But if you separate them between another type of menu item then only radio menus next to each other behave as the on radio group. Here are some examples and code snippets.
Three Radio Buttons in one Group
await chrome.contextMenus.create({
id: 'menu-types',
title: 'Menu Types',
type: 'normal',
contexts: ['page'],
});
await chrome.contextMenus.create({
id: 'radio1',
parentId: "menu-types",
title: 'Radio 1',
type: "radio",
contexts: ['page'],
});
await chrome.contextMenus.create({
id: 'radio2',
parentId: "menu-types",
title: 'Radio 2',
type: "radio",
contexts: ['page'],
});
await chrome.contextMenus.create({
id: 'radio3',
parentId: "menu-types",
title: 'Radio 3',
type: "radio",
contexts: ['page'],
});
Two Groups of Radio Menus Items
Here are two groups of radio
menu items that have been separated by other type of menu item
chrome.contextMenus.create({
id: 'radio1',
title: 'Radio 1',
type: "radio",
contexts: ['page'],
});
chrome.contextMenus.create({
id: 'radio2',
title: 'Radio 2',
type: "radio",
contexts: ['page'],
});
// separates here...
chrome.contextMenus.create({
id: 's',
title: 'Separator',
type: "separator",
contexts: ['page'],
});
chrome.contextMenus.create({
id: 'radio3',
title: 'Radio 3',
type: "radio",
contexts: ['page'],
});
chrome.contextMenus.create({
id: 'radio4',
title: 'Radio 4',
type: "radio",
contexts: ['page'],
});
When Menu menu is clicked
Now lets cover one of the most crucial parts on the chrome.contextMenus
API the onclick listener. We can make all the sorts of menu items until kingdom come, but it serves no purpose unless happens when the menu item is clicked.
How we write in on code. You would put this in your background script.
chrome.contextMenus.onClicked.addListener((info, tab) => {
let checked = info.checked;
let editable = info.editable;
let linkUrl = info.linkUrl;
let mediaType = info.mediaType;
let menuItemId = info.menuItemId;
let pageUrl = info.pageUrl;
let selectionText = info.selectionText;
let parentMenuItemId = info.parentMenuItemId;
let wasChecked = info.wasChecked;
});
You can refer here, to know what kind of data info
and tab
hold. If you want more information in the tab
argument, you’d need to set the tabs
permission.
Sample Project
The Google Chrome Team provide source code to a sample project you can run for yourself here: https://github.com/GoogleChrome/chrome-extensions-samples/tree/main/api-samples/contextMenus.
Leave a Reply