Chrome扩展:如何根据点击链接动态更新上下文菜单(替代menus.onShown)
Great question! You're right that Chrome doesn't currently support the menus.onShown API that Firefox offers for dynamically updating context menu items based on the target link's URL or text. But there are a couple of solid workarounds to achieve the same effect.
Method 1: Update Menu Item Text on Context Menu Trigger
The core idea here is to listen for the contextmenu event in a content script, grab the target link's details, and send that data to your background script to update the menu item's title right before the menu appears.
Step 1: Configure Manifest Permissions
First, make sure your manifest.json includes the necessary permissions for context menus, content scripts, and scripting:
{ "manifest_version": 3, "name": "Dynamic Link Context Menu", "version": "1.0", "permissions": ["contextMenus", "activeTab", "scripting"], "background": { "service_worker": "background.js" } }
Step 2: Initialize Menu in Background Script
Create a base context menu item when your extension installs:
// background.js chrome.runtime.onInstalled.addListener(() => { chrome.contextMenus.create({ id: "dynamic-link-action", title: "Default Action", contexts: ["link"] // Only show on links }); }); // Listen for messages from content scripts to update the menu chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.type === "updateMenuTitle") { chrome.contextMenus.update("dynamic-link-action", { title: `Open "${message.linkText}" (${message.linkUrl.slice(0, 50)}...)` }); } });
Step 3: Trigger Update from Content Script
Add a content script that listens for the right-click event, checks if the target is a link, and sends the link's details to the background:
// content-script.js document.addEventListener("contextmenu", (event) => { const targetLink = event.target.closest("a"); if (targetLink?.href) { chrome.runtime.sendMessage({ type: "updateMenuTitle", linkText: targetLink.textContent.trim(), linkUrl: targetLink.href }); } });
Notes for This Method
- This approach works in most cases because the
contextmenuevent fires just before the menu is displayed, giving the background script time to update the title. - Be mindful of edge cases: if a user right-clicks extremely quickly, there might be a tiny delay, but this is rare in practice.
- Use
closest("a")instead of checkingevent.target.tagNamedirectly to handle links with nested elements (e.g.,<a><span>Link Text</span></a>).
Method 2: Dynamically Create/Delete Menu Items
If you need more flexibility, you can delete existing menu items and create new ones tailored to the target link when the user right-clicks.
Example Implementation
Background Script
// background.js chrome.runtime.onInstalled.addListener(() => { // No initial menu needed—we'll create it dynamically }); // Handle menu clicks chrome.contextMenus.onClicked.addListener((info, tab) => { if (info.menuItemId === "dynamic-link-custom") { // Do something with the link, e.g., open in new tab chrome.tabs.create({ url: info.linkUrl }); } }); // Listen for requests to create the dynamic menu chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.type === "createDynamicMenu") { // First remove any existing dynamic menu item chrome.contextMenus.remove("dynamic-link-custom", () => { // Ignore errors if the item doesn't exist chrome.contextMenus.create({ id: "dynamic-link-custom", title: `Open Link: ${message.linkText}`, contexts: ["link"], linkUrl: message.linkUrl }); }); } });
Content Script
Same as Method 1, but send a "createDynamicMenu" message instead:
// content-script.js document.addEventListener("contextmenu", (event) => { const targetLink = event.target.closest("a"); if (targetLink?.href) { chrome.runtime.sendMessage({ type: "createDynamicMenu", linkText: targetLink.textContent.trim(), linkUrl: targetLink.href }); } });
Notes for This Method
- This avoids any race conditions with updating existing items, as you're creating a fresh item each time.
- If your extension has multiple context menu items, use
chrome.contextMenus.removewith specific IDs instead ofremoveAllto avoid deleting unrelated items.
Key Considerations
- Permissions: Ensure you have the
contextMenuspermission, plusactiveTabor<all_urls>to inject content scripts into the pages where you want the dynamic menu to work. - Manifest V3 vs V2: The examples above use Manifest V3 (the current standard for Chrome extensions). If you're still on V2, adjust the background script syntax (replace service workers with background pages) and content script injection methods.
内容的提问来源于stack exchange,提问作者Alexey Romanov

