Now I am trying to make a google chrome extension that controls google documents. I'd like to add a new item on google document's context menu using chrome extension. When you click the right mouse button on google document, you can see the context menu. Using chrome extension, I'd like to add a new item like "Subscribe" on that menu. How can I do this?
Asked
Active
Viewed 739 times
2
-
Welcome to Stack Overflow! Please read the [help pages](https://stackoverflow.com/help), take the [SO tour](https://stackoverflow.com/tour), read about [how to ask good questions](https://stackoverflow.com/help/how-to-ask), as well as this [question checklist](https://codeblog.jonskeet.uk/2012/11/24/stack-overflow-question-checklist/). Also please learn how to create a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). If you want help on a programming forum, it works best if you post code and/or couch your question in the context of the docs :) – Ross Jacobs Oct 03 '19 at 05:09
-
This is a standard DOM manipulation task, there's no specific extension API for this. Assuming you know JavaScript and DOM simply declare a [content script](https://developer.chrome.com/extensions/content_scripts) with a listener for "contextmenu" event, inside of which use MutationObserver to wait for the menu to appear and add your DOM element with a click listener that does what you want. – wOxxOm Oct 03 '19 at 06:27
1 Answers
0
Following wOxxOm's instructions:
First, we need to establish a content script, by adding the following code to your manifest.json
"permissions": [
"https://docs.google.com/*"
],
"content_scripts": [
{
"matches": [
"https://docs.google.com/*"
],
"js": [
"scripts/content.js"
],
"run_at": "document_end"
}
],
and then in your content.js
function filterHiddenElements(nodeList) {
return Array.from(nodeList).filter(v=>v.style.display !== "none" && !["hidden", "collapse"].includes(v.style.visibility));
}
function getContextMenuElement() {
// there are many divs that match the css selector, but only one will be visible.
const contextMenus = filterHiddenElements(document.querySelectorAll(".goog-menu.goog-menu-vertical.apps-menu-hide-mnemonics"));
// return only the visible ones
return contextMenus.length > 0 ? contextMenus[0] : null;
}
function contextMenuEventHandler() {
const id = "custom-context-menu-id";
const customContextMenuName = "Custom Name";
const customContextMenuHint = "Custom Hint";
const contextMenuElement = getContextMenuElement();
if (contextMenuElement) {
const preExisting = document.querySelector("#" + id);
if (preExisting) {
// we need to remove the preExisting element because google docs removes all elements and recreates them.
// it will remain at the top then next time if we don't do this
preExisting.parentElement.removeChild(preExisting);
}
const separators = filterHiddenElements(contextMenuElement.querySelectorAll(".apps-hoverable-menu-separator-container"));
if (separators.length) {
// you can also put a custom icon in place of .docs-icon-img-container
const innerHTML = `
<div class="goog-menuitem-content">
<div class="docs-icon goog-inline-block goog-menuitem-icon" aria-hidden="true">
<div class="docs-icon-img-container docs-icon-img docs-icon-cut">
</div>
</div>
<span class="goog-menuitem-label">
${customContextMenuName}
</span>
<span class="goog-menuitem-accel" aria-label="⌘X">
${customContextMenuHint}
</span>
</div>`;
// this can't be HTML text because it will handle the events like hover and click
const div = document.createElement("div");
div.innerHTML = innerHTML;
div.className = "goog-menuitem apps-menuitem";
div.id = id;
div.setAttribute("role", "menuitem");
// hover events
div.addEventListener("mouseenter", e=>{
e.target.classList.add("goog-menuitem-highlight");
}
);
div.addEventListener("mouseleave", e=>{
e.target.classList.remove("goog-menuitem-highlight");
}
);
// click event
div.addEventListener("click", onClickEventHandler);
// put it above the first separator
separators[0].parentElement.insertBefore(div, separators[0]);
}
} else {
console.log("Could not find context menu");
}
}
function onClickEventHandler(e) {
alert(`#${e.target.id} has been clicked!`);
}
document.body.addEventListener('contextmenu', contextMenuEventHandler);
Disclaimer: Because this is not following an official API, this is a very hacky way to do it and is likely to break. However, fixes shouldn't be that hard if you run into problems. It is working as of May 2021.

IcyIcicle
- 564
- 3
- 15
-
A working implementation of the above code is included as a part of this chrome extension I made: https://chrome.google.com/webstore/detail/quire-anywhere/nojpnnfpfaepolalpokjlblonedknfgf – IcyIcicle Jun 07 '21 at 00:43