create-gojs-kit
Version:
A CLI for downloading GoJS samples, extensions, and docs
592 lines (537 loc) • 26.8 kB
HTML
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"/>
<meta name="description" content="Interactive GoJS diagram demonstrating creating new nodes and links, reconnecting links, grouping and ungrouping, and context menus and tooltips for nodes, for links, and for the diagram background." />
<meta itemprop="description" content="Interactive GoJS diagram demonstrating creating new nodes and links, reconnecting links, grouping and ungrouping, and context menus and tooltips for nodes, for links, and for the diagram background." />
<meta property="og:description" content="Interactive GoJS diagram demonstrating creating new nodes and links, reconnecting links, grouping and ungrouping, and context menus and tooltips for nodes, for links, and for the diagram background." />
<meta name="twitter:description" content="Interactive GoJS diagram demonstrating creating new nodes and links, reconnecting links, grouping and ungrouping, and context menus and tooltips for nodes, for links, and for the diagram background." />
<link rel="preconnect" href="https://rsms.me/">
<link rel="stylesheet" href="../assets/css/style.css">
<!-- Copyright 1998-2025 by Northwoods Software Corporation. -->
<meta itemprop="name" content="Basic Sample Showing ToolTips and Context Menus for Nodes, Links, Groups, and Diagram" />
<meta property="og:title" content="Basic Sample Showing ToolTips and Context Menus for Nodes, Links, Groups, and Diagram" />
<meta name="twitter:title" content="Basic Sample Showing ToolTips and Context Menus for Nodes, Links, Groups, and Diagram" />
<meta property="og:image" content="https://gojs.net/latest/assets/images/screenshots/basic.png" />
<meta itemprop="image" content="https://gojs.net/latest/assets/images/screenshots/basic.png" />
<meta name="twitter:image" content="https://gojs.net/latest/assets/images/screenshots/basic.png" />
<meta property="og:url" content="https://gojs.net/latest/samples/basic.html" />
<meta property="twitter:url" content="https://gojs.net/latest/samples/basic.html" />
<meta name="twitter:card" content="summary_large_image" />
<meta property="og:type" content="website" />
<meta property="twitter:domain" content="gojs.net" />
<title>
Basic Sample Showing ToolTips and Context Menus for Nodes, Links, Groups, and Diagram | GoJS Diagramming Library
</title>
</head>
<body>
<!-- This top nav is not part of the sample code -->
<nav id="navTop" class=" w-full h-[var(--topnav-h)] z-30 bg-white border-b border-b-gray-200">
<div class="max-w-screen-xl mx-auto flex flex-wrap items-start justify-between px-4">
<a class="text-white bg-nwoods-primary font-bold !leading-[calc(var(--topnav-h)_-_1px)] my-0 px-2 text-4xl lg:text-5xl logo"
href="../">
GoJS
</a>
<div class="relative">
<button id="topnavButton" class="h-[calc(var(--topnav-h)_-_1px)] px-2 m-0 text-gray-900 bg-inherit shadow-none md:hidden hover:!bg-inherit hover:!text-nwoods-accent hover:!shadow-none" aria-label="Navigation">
<svg class="h-7 w-7 block" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
<div id="topnavList" class="hidden md:block">
<div class="absolute right-0 z-30 flex flex-col items-end rounded border border-gray-200 p-4 pl-12 shadow bg-white text-gray-900 font-semibold
md:flex-row md:space-x-4 md:items-start md:border-0 md:p-0 md:shadow-none md:bg-inherit">
<a href="../learn/">Learn</a>
<a href="../samples/">Samples</a>
<a href="../intro/">Intro</a>
<a href="../api/">API</a>
<a href="../download.html">Download</a>
<a href="https://forum.nwoods.com/c/gojs/11" target="_blank" rel="noopener">Forum</a>
<a id="tc" href="https://nwoods.com/contact.html"
target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/contact.html', 'contact');">Contact</a>
<a id="tb" href="https://nwoods.com/sales/index.html"
target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/sales/index.html', 'buy');">Buy</a>
</div>
</div>
</div>
</div>
</nav>
<script>
window.addEventListener("DOMContentLoaded", function () {
// topnav
var topButton = document.getElementById("topnavButton");
var topnavList = document.getElementById("topnavList");
if (topButton && topnavList) {
topButton.addEventListener("click", function (e) {
topnavList
.classList
.toggle("hidden");
e.stopPropagation();
});
document.addEventListener("click", function (e) {
// if the clicked element isn't the list, close the list
if (!topnavList.classList.contains("hidden") && !e.target.closest("#topnavList")) {
topButton.click();
}
});
// set active <a> element
var url = window
.location
.href
.toLowerCase();
var aTags = topnavList.getElementsByTagName('a');
for (var i = 0; i < aTags.length; i++) {
var lowerhref = aTags[i]
.href
.toLowerCase();
if (lowerhref.endsWith('.html'))
lowerhref = lowerhref.slice(0, -5);
if (url.startsWith(lowerhref)) {
aTags[i]
.classList
.add('active');
break;
}
}
}
});
</script>
<div class="flex flex-col prose">
<div class="w-full max-w-screen-xl mx-auto">
<!-- * * * * * * * * * * * * * -->
<!-- Start of GoJS sample code -->
<script src="https://cdn.jsdelivr.net/npm/gojs@3.1.0"></script>
<div id="allSampleContent" class="p-4 w-full">
<script id="code">
function init() {
myDiagram = new go.Diagram('myDiagramDiv', { // create a Diagram for the DIV HTML element
// allow double-click in background to create a new node
'clickCreatingTool.archetypeNodeData': { text: 'Node', color: 'white' },
// allow Ctrl-G to call groupSelection()
'commandHandler.archetypeGroupData': { text: 'Group', isGroup: true, color: 'blue' },
// enable undo & redo
'undoManager.isEnabled': true
});
// Define the appearance and behavior for Nodes:
// First, define the shared context menu for all Nodes, Links, and Groups.
// To simplify this code we define a function for creating a context menu button:
function makeButton(text, action, visiblePredicate) {
const button =
go.GraphObject.build('ContextMenuButton')
.add(new go.TextBlock(text, { click: action }));
// don't bother with binding GraphObject.visible if there's no predicate
if (visiblePredicate) {
button.bindObject('visible', '', (o, e) => o.diagram ? visiblePredicate(o, e) : false);
}
return button;
}
// a context menu is an Adornment with a bunch of buttons in them
var partContextMenu =
go.GraphObject.build('ContextMenu')
.add(
makeButton('Properties',
(e, obj) => {
// OBJ is this Button
var contextmenu = obj.part; // the Button is in the context menu Adornment
var part = contextmenu.adornedPart; // the adornedPart is the Part that the context menu adorns
// now can do something with PART, or with its data, or with the Adornment (the context menu)
if (part instanceof go.Link) alert(linkInfo(part.data));
else if (part instanceof go.Group) alert(groupInfo(contextmenu));
else alert(nodeInfo(part.data));
}),
makeButton('Cut',
(e, obj) => e.diagram.commandHandler.cutSelection(),
o => o.diagram.commandHandler.canCutSelection()
),
makeButton('Copy',
(e, obj) => e.diagram.commandHandler.copySelection(),
o => o.diagram.commandHandler.canCopySelection()
),
makeButton('Paste',
(e, obj) =>
e.diagram.commandHandler.pasteSelection(
e.diagram.toolManager.contextMenuTool.mouseDownPoint
),
o =>
o.diagram.commandHandler.canPasteSelection(
o.diagram.toolManager.contextMenuTool.mouseDownPoint
)
),
makeButton('Delete',
(e, obj) => e.diagram.commandHandler.deleteSelection(),
o => o.diagram.commandHandler.canDeleteSelection()
),
makeButton('Undo',
(e, obj) => e.diagram.commandHandler.undo(),
o => o.diagram.commandHandler.canUndo()
),
makeButton('Redo',
(e, obj) => e.diagram.commandHandler.redo(),
o => o.diagram.commandHandler.canRedo()
),
makeButton('Group',
(e, obj) => e.diagram.commandHandler.groupSelection(),
o => o.diagram.commandHandler.canGroupSelection()
),
makeButton('Ungroup',
(e, obj) => e.diagram.commandHandler.ungroupSelection(),
o => o.diagram.commandHandler.canUngroupSelection()
)
);
function nodeInfo(d) {
// Tooltip info for a node data object
var str = 'Node ' + d.key + ': ' + d.text + '\n';
if (d.group) str += 'member of ' + d.group;
else str += 'top-level node';
return str;
}
// These nodes have text surrounded by a rounded rectangle
// whose fill color is bound to the node data.
// The user can drag a node by dragging its TextBlock label.
// Dragging from the Shape will start drawing a new link.
myDiagram.nodeTemplate =
new go.Node('Auto', {
locationSpot: go.Spot.Center,
// this tooltip Adornment is shared by all nodes
toolTip:
go.GraphObject.build('ToolTip')
.add(
new go.TextBlock({ margin: 4 }) // the tooltip shows the result of calling nodeInfo(data)
.bind('text', '', nodeInfo)
),
// this context menu Adornment is shared by all nodes
contextMenu: partContextMenu
})
.add(
new go.Shape('RoundedRectangle', {
fill: 'white', // the default fill, if there is no data bound value
portId: '',
cursor: 'pointer', // the Shape is the port, not the whole Node
// allow all kinds of links from and to this port
fromLinkable: true,
fromLinkableSelfNode: true,
fromLinkableDuplicates: true,
toLinkable: true,
toLinkableSelfNode: true,
toLinkableDuplicates: true
})
.bind('fill', 'color'),
new go.TextBlock({
font: 'bold 14px sans-serif',
stroke: '#333',
margin: 6, // make some extra space for the shape around the text
isMultiline: false, // don't allow newlines in text
editable: true // allow in-place editing by user
})
.bindTwoWay('text', 'text')
);
// Define the appearance and behavior for Links:
function linkInfo(d) {
// Tooltip info for a link data object
return 'Link:\nfrom ' + d.from + ' to ' + d.to;
}
// The link shape and arrowhead have their stroke brush data bound to the "color" property
myDiagram.linkTemplate =
new go.Link({
toShortLength: 3,
relinkableFrom: true,
relinkableTo: true,
// this tooltip Adornment is shared by all links
toolTip:
go.GraphObject.build('ToolTip')
.add(
new go.TextBlock({ margin: 4 }) // the tooltip shows the result of calling linkInfo(data)
.bind('text', '', linkInfo)
),
// the same context menu Adornment is shared by all links
contextMenu: partContextMenu
}) // allow the user to relink existing links
.add(
new go.Shape({ strokeWidth: 2 })
.bind('stroke', 'color'),
new go.Shape({ toArrow: 'Standard', stroke: null })
.bind('fill', 'color')
);
// Define the appearance and behavior for Groups:
function groupInfo(adornment) {
// takes the tooltip or context menu, not a group node data object
var g = adornment.adornedPart; // get the Group that the tooltip adorns
var mems = g.memberParts.count;
var links = 0;
g.memberParts.each(part => {
if (part instanceof go.Link) links++;
});
return (
'Group ' +
g.data.key +
': ' +
g.data.text +
'\n' +
mems +
' members including ' +
links +
' links'
);
}
// Groups consist of a title in the color given by the group node data
// above a translucent gray rectangle surrounding the member parts
myDiagram.groupTemplate =
new go.Group('Vertical', {
selectionObjectName: 'PANEL', // selection handle goes around shape, not label
ungroupable: true, // enable Ctrl-Shift-G to ungroup a selected Group
// this tooltip Adornment is shared by all groups
toolTip:
go.GraphObject.build('ToolTip')
.add(
new go.TextBlock({ margin: 4 })
// bind to tooltip, not to Group.data, to allow access to Group properties
.bindObject('text', '', groupInfo)
),
// the same context menu Adornment is shared by all groups
contextMenu: partContextMenu
})
.add(
new go.TextBlock({
//alignment: go.Spot.Right,
font: 'bold 19px sans-serif',
isMultiline: false, // don't allow newlines in text
editable: true // allow in-place editing by user
})
.bindTwoWay('text', 'text')
.bind('stroke', 'color'),
new go.Panel('Auto', { name: 'PANEL' })
.add(
new go.Shape('Rectangle', {
// the rectangular shape around the members
fill: 'rgba(128,128,128,0.2)',
stroke: 'gray',
strokeWidth: 3,
portId: '',
cursor: 'pointer', // the Shape is the port, not the whole Node
// allow all kinds of links from and to this port
fromLinkable: true,
fromLinkableSelfNode: true,
fromLinkableDuplicates: true,
toLinkable: true,
toLinkableSelfNode: true,
toLinkableDuplicates: true
}),
new go.Placeholder({ margin: 10, background: 'transparent' })
) // represents where the members are
);
// Define the behavior for the Diagram background:
function diagramInfo(model) {
// Tooltip info for the diagram's model
return (
'Model:\n' + model.nodeDataArray.length + ' nodes, ' + model.linkDataArray.length + ' links'
);
}
// provide a tooltip for the background of the Diagram, when not over any Part
myDiagram.toolTip =
go.GraphObject.build('ToolTip')
.add(
new go.TextBlock({ margin: 4 })
.bind('text', '', diagramInfo)
);
// provide a context menu for the background of the Diagram, when not over any Part
myDiagram.contextMenu =
go.GraphObject.build('ContextMenu')
.add(
makeButton('Paste',
(e, obj) =>
e.diagram.commandHandler.pasteSelection(
e.diagram.toolManager.contextMenuTool.mouseDownPoint
),
o =>
o.diagram.commandHandler.canPasteSelection(
o.diagram.toolManager.contextMenuTool.mouseDownPoint
)
),
makeButton('Undo',
(e, obj) => e.diagram.commandHandler.undo(),
o => o.diagram.commandHandler.canUndo()
),
makeButton('Redo',
(e, obj) => e.diagram.commandHandler.redo(),
o => o.diagram.commandHandler.canRedo()
)
);
// Create the Diagram's Model:
const nodeDataArray = [
{ key: 1, text: 'Alpha', color: 'lightblue' },
{ key: 2, text: 'Beta', color: 'orange' },
{ key: 3, text: 'Gamma', color: 'lightgreen', group: 5 },
{ key: 4, text: 'Delta', color: 'pink', group: 5 },
{ key: 5, text: 'Epsilon', color: 'green', isGroup: true }
];
const linkDataArray = [
{ from: 1, to: 2, color: 'blue' },
{ from: 2, to: 2 },
{ from: 3, to: 4, color: 'green' },
{ from: 3, to: 1, color: 'purple' }
];
myDiagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
}
window.addEventListener('DOMContentLoaded', init);
</script>
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width: 400px; height: 400px"></div>
<p>
This sample demonstrates tooltips and context menus for all parts and for the diagram
background, as well as several other powerful diagram editing abilities.
</p>
<p>
Unlike the <a href="minimal.html">Minimal</a> sample, this sample has templates for Links and
for Groups, plus tooltips and context menus for Nodes, for Links, for Groups, and for the
Diagram.
</p>
<p>
This sample has all of the functionality of the Minimal sample, but additionally allows the user
to:
</p>
<ul>
<li>create new nodes: double-click in the background of the diagram</li>
<li>edit text: select the node and then click on the text, or select the node and press F2</li>
<li>draw new links: drag from the inner edge of the node's or the group's shape</li>
<li>
reconnect existing links: select the link and then drag the diamond-shaped handle at either
end of the link
</li>
<li>
group nodes and links: select some nodes and links and then type Ctrl-G (or invoke via context
menu)
</li>
<li>
ungroup an existing group: select a group and then type Ctrl-Shift-G (or invoke via context
menu)
</li>
</ul>
<p>
GoJS contains many other possible commands, which can be invoked by either mouse/keyboard/touch
or programmatically.
<a href="../intro/commands.html">See an overview of possible commands here.</a>
On a Mac, use CMD instead of Ctrl.
</p>
<p>
On touch devices, hold your finger stationary to bring up a context menu. The default context
menu supports most of the standard commands that are enabled at that time for that object.
</p>
</div>
</div>
<!-- * * * * * * * * * * * * * -->
<!-- End of GoJS sample code -->
</div>
<div id="allTagDescriptions" class="p-4 w-full max-w-screen-xl mx-auto">
<hr/>
<h3 class="text-xl">GoJS Features in this sample</h3>
<!-- blacklist tags that do not correspond to a specific GoJS feature -->
<h4>Groups</h4>
<p>
The <a href="../api/symbols/Group.html" target="api">Group</a> class is used to treat a collection of <a href="../api/symbols/Node.html" target="api">Node</a>s and <a href="../api/symbols/Link.html" target="api">Link</a>s as if they were a single <a href="../api/symbols/Node.html" target="api">Node</a>.
Those nodes and links are members of the group; together they constitute a subgraph.
</p>
<p>
A subgraph is <em>not</em> another <a href="../api/symbols/Diagram.html" target="api">Diagram</a>, so there is no separate HTML Div element for the subgraph of a group.
All of the <a href="../api/symbols/Part.html" target="api">Part</a>s that are members of a <a href="../api/symbols/Group.html" target="api">Group</a> belong to the same Diagram as the Group.
There can be links between member nodes and nodes outside of the group as well as links between the group itself and other nodes.
There can even be links between member nodes and the containing group itself.
</p>
<p>
More information can be found in the <a href="../intro/groups.html">GoJS Intro</a>.
</p>
<p>
<a href="../samples/index.html#groups">Related samples</a>
</p>
<hr>
<!-- blacklist tags that do not correspond to a specific GoJS feature -->
<h4>ToolTips</h4>
<p>
A tooltip is an <a href="../api/symbols/Adornment.html" target="api">Adornment</a> that is shown when the mouse hovers over an object that has its <a href="../api/symbols/GraphObject.html#toolTip" target="api">GraphObject.toolTip</a> set.
The tooltip part is bound to the same data as the part itself.
</p>
<p>
It is typical to implement a tooltip as a "ToolTip" Panel holding a <a href="../api/symbols/TextBlock.html" target="api">TextBlock</a> or a Panel of TextBlocks and other objects.
Each "ToolTip" is just an "Auto" Panel <a href="../api/symbols/Adornment.html" target="api">Adornment</a> that is shadowed, and where the border is a rectangular <a href="../api/symbols/Shape.html" target="api">Shape</a> with a light gray fill.
However you can implement the tooltip as any arbitrarily complicated Adornment.
</p>
<p>
More information can be found in the <a href="../intro/tooltips.html">GoJS Intro</a>.
</p>
<p>
<a href="../samples/index.html#tooltips">Related samples</a>
</p>
<hr>
<!-- blacklist tags that do not correspond to a specific GoJS feature -->
<h4>Context Menus</h4>
<p>
A GoJS context menu is an <a href="../api/symbols/Adornment.html" target="api">Adornment</a> that is shown when the user context-clicks (right mouse click or long touch hold)
an object that has its <a href="../api/symbols/GraphObject.html#contextMenu" target="api">GraphObject.contextMenu</a> set.
The context menu is bound to the same data as the part itself.
</p>
<p>
It is typical to implement a context menu as a "ContextMenu" Panel containing "ContextMenuButton"s,
as you can see in the code below in the assignment of the Node's <a href="../api/symbols/GraphObject.html#contextMenu" target="api">GraphObject.contextMenu</a> and <a href="../api/symbols/Diagram.html#contextMenu" target="api">Diagram.contextMenu</a> properties.
Each "ContextMenu" is just a "Vertical" Panel <a href="../api/symbols/Adornment.html" target="api">Adornment</a> that is shadowed.
Each "ContextMenuButton" is a Panel on which you can set the <a href="../api/symbols/GraphObject.html#click" target="api">GraphObject.click</a> event handler.
In the event handler <code>obj.part</code> will be the whole context menu Adornment.
<code>obj.part.adornedPart</code> will be the adorned Node or Link.
The bound data is <code>obj.part.data</code>, which will be the same as <code>obj.part.adornedPart.data</code>.
</p>
<p>
More information can be found in the <a href="../intro/contextmenus.html">GoJS Intro</a>.
</p>
<p>
<a href="../samples/index.html#contextmenus">Related samples</a>
</p>
<hr>
<!-- blacklist tags that do not correspond to a specific GoJS feature -->
<h4>Buttons</h4>
<p>
GoJS defines several <a href="../api/symbols/Panel.html" target="api">Panel</a>s for common uses.
These include "Button", "TreeExpanderButton", "SubGraphExpanderButton", "PanelExpanderButton", "ContextMenuButton", and "CheckBoxButton".
"ContextMenuButton"s are typically used inside of "ContextMenu" Panels;
"CheckBoxButton"s are used in the implementation of "CheckBox" Panels.
</p>
<p>
These predefined panels can be used as if they were <a href="../api/symbols/Panel.html" target="api">Panel</a>-derived classes in calls to <a href="../api/symbols/GraphObject.html#build" target="api">GraphObject.build</a>.
They are implemented as simple visual trees of <a href="../api/symbols/GraphObject.html" target="api">GraphObject</a>s in <a href="../api/symbols/Panel.html" target="api">Panel</a>s,
with pre-set properties and event handlers.
</p>
<p>
More information can be found in the <a href="../intro/buttons.html">GoJS Intro</a>.
</p>
<p>
<a href="../samples/index.html#buttons">Related samples</a>
</p>
<hr>
<!-- blacklist tags that do not correspond to a specific GoJS feature -->
<h4>Commands</h4>
<p>
A <a href="../api/symbols/CommandHandler.html" target="api">CommandHandler</a> handles all default keyboard input events in a Diagram.
There are many predefined methods on <a>CommandHandler</a> that implement common commands to operate on the Diagram or the current <a>Diagram.selection></a>.
</p>
<p>
You can override <a>CommandHandler.doKeyDown</a> to handle additional keyboard shortcuts or to change which commands are invoked via the keyboard.
<p>
Your code can invoke a command by calling the appropriate method on the <a>Diagram.commandHandler</a>.
Each command method has a corresponding <b>can...</b> predicate that your code can use to enable or disable any buttons that invoke the command.
Your code can customize the behavior of a command by overriding the method on <a>CommandHandler</a>,
or by setting properties on the <a>CommandHandler</a> or <a>Diagram</a> or <a>Part</a>s --
see <a href="../intro/permissions.html">GoJS Permissions</a>.
</p>
<p>
There are several CommandHandler extensions that provide additional functionality.
<p>
More information can be found in the <a href="../intro/commands.html">GoJS Intro</a>.
</p>
<p>
<a href="../samples/index.html#commands">Related samples</a>
</p>
<hr>
</div>
</div>
</body>
<!-- This script is part of the gojs.net website, and is not needed to run the sample -->
<script src="../assets/js/goSamples.js"></script>
</html>