OK, here’s a complete sample that shows an HTML context menu without making use of the ContextMenuTool nor the GraphObject.contextMenu or Diagram.contextMenu properties.
There’s no call to stopPropagation
anywhere, but there is a “contextmenu” DOM event listener that calls preventDefault
. There’s no setting of any InputEvent property, either.
<!DOCTYPE html>
<html>
<head>
<title>Minimal GoJS Sample</title>
<!-- Copyright 1998-2021 by Northwoods Software Corporation. -->
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://unpkg.com/gojs"></script>
<style type="text/css">
/* CSS for the traditional context menu, copied from https://gojs.net/latest/samples/customContextMenu.html */
.menu {
display: none;
position: absolute;
opacity: 0;
margin: 0;
padding: 8px 0;
z-index: 999;
box-shadow: 0 5px 5px -3px rgba(0, 0, 0, .2), 0 8px 10px 1px rgba(0, 0, 0, .14), 0 3px 14px 2px rgba(0, 0, 0, .12);
list-style: none;
background-color: #ffffff;
border-radius: 4px;
}
.menu-item {
display: block;
position: relative;
min-width: 60px;
margin: 0;
padding: 6px 16px;
font: bold 12px sans-serif;
color: rgba(0, 0, 0, .87);
cursor: pointer;
}
.menu-item::before {
position: absolute;
top: 0;
left: 0;
opacity: 0;
pointer-events: none;
content: "";
width: 100%;
height: 100%;
background-color: #000000;
}
.menu-item:hover::before {
opacity: .04;
}
.menu .menu {
top: -8px;
left: 100%;
}
.show-menu, .menu-item:hover > .menu {
display: block;
opacity: 1;
}
</style>
<script id="code">
function init() {
var $ = go.GraphObject.make;
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
contextClick: function(e) { showContextMenu(null, e.diagram); },
"undoManager.isEnabled": true
});
// This is the actual HTML context menu.
// Copied from https://gojs.net/latest/samples/customContextMenu.html
var cxElement = document.getElementById("contextMenu");
// We don't want the div acting as a context menu to have a (browser) context menu!
cxElement.addEventListener("contextmenu", function(e) {
e.preventDefault();
return false;
}, false);
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{
contextClick: function(e, node) { showContextMenu(node, e.diagram); }
},
new go.Binding("location", "loc").makeTwoWay(),
$(go.Shape, { fill: "white" }),
$(go.TextBlock,
{ margin: 8 },
new go.Binding("text", "key"))
);
myDiagram.model =
$(go.GraphLinksModel, {
linkKeyProperty: "k",
nodeDataArray: [
{ key: 1 },
{ key: 2 },
{ key: 3 },
{ key: 4 }
],
linkDataArray: [
{ from: 1, to: 2 },
{ from: 1, to: 3 },
{ from: 3, to: 4 }
]});
}
// Copied from https://gojs.net/latest/samples/customContextMenu.html
// and adapted not to use the ContextMenuTool.
function showContextMenu(obj, diagram) {
// This is the actual HTML context menu:
var cxElement = document.getElementById("contextMenu");
// Show only the relevant buttons given the current state.
var cmd = diagram.commandHandler;
var hasMenuItem = false;
function maybeShowItem(elt, pred) {
if (pred) {
elt.style.display = "block";
hasMenuItem = true;
} else {
elt.style.display = "none";
}
}
maybeShowItem(document.getElementById("cut"), cmd.canCutSelection());
maybeShowItem(document.getElementById("copy"), cmd.canCopySelection());
maybeShowItem(document.getElementById("paste"), cmd.canPasteSelection(diagram.toolManager.contextMenuTool.mouseDownPoint));
maybeShowItem(document.getElementById("delete"), cmd.canDeleteSelection());
// Now show the whole context menu element
if (hasMenuItem) {
cxElement.classList.add("show-menu");
// we don't bother overriding positionContextMenu, we just do it here:
var mousePt = diagram.lastInput.viewPoint;
cxElement.style.left = mousePt.x + 5 + "px";
cxElement.style.top = mousePt.y + "px";
}
// Optional: Use a `window` click listener with event capture to
// remove the context menu if the user clicks elsewhere on the page
window.addEventListener("click", hideContextMenu, true);
}
function hideContextMenu() {
// This is the actual HTML context menu:
var cxElement = document.getElementById("contextMenu");
cxElement.classList.remove("show-menu");
// Optional: Use a `window` click listener with event capture to
// remove the context menu if the user clicks elsewhere on the page
window.removeEventListener("click", hideContextMenu, true);
}
// This is the general menu command handler, parameterized by the name of the command.
function cxcommand(event, val) {
if (val === undefined) val = event.currentTarget.id;
var diagram = myDiagram;
switch (val) {
case "cut": diagram.commandHandler.cutSelection(); break;
case "copy": diagram.commandHandler.copySelection(); break;
case "paste": diagram.commandHandler.pasteSelection(diagram.lastInput.documentPoint); break;
case "delete": diagram.commandHandler.deleteSelection(); break;
}
hideContextMenu();
}
</script>
</head>
<body onload="init()">
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
<ul id="contextMenu" class="menu">
<li id="cut" class="menu-item" onclick="cxcommand(event)">Cut</li>
<li id="copy" class="menu-item" onclick="cxcommand(event)">Copy</li>
<li id="paste" class="menu-item" onclick="cxcommand(event)">Paste</li>
<li id="delete" class="menu-item" onclick="cxcommand(event)">Delete</li>
</ul>
</body>
</html>
I was never able to see the browser’s context menu.