I would like to add a scrollbar to the tooltip when it contains a large amount of text.
Code:-
Tooltip:-
I would like to add a scrollbar to the tooltip when it contains a large amount of text.
Code:-
Tooltip:-
ToolTips aren’t normally intended to be interactive. Still, one can implement such functionality.
Try the following code, which defines a “ScrollingText” builder, and which uses that builder both inside a Node template and in a toolTip Adornment.
Note that when the user causes a tooltip to be shown, if they move the mouse out of the GraphObject with the GraphObject.toolTip, the tooltip automatically disappears.
Tooltips also automatically disappear after ToolManager.toolTipDuration milliseconds. (The default is 5000 – five seconds.)
In version 3.1 “ToolTip” Adornments will automatically extend how long the tooltip will remain visible as long as GraphObject.mouseOver events occur before the expiration.
Also in version 3.1, the “AutoRepeatButton” will be built into the GoJS library, so you won’t need to import the “ScrollingTable” extension or copy its definition into your code.
<!DOCTYPE html>
<html>
<head>
<title>Scrolling Text</title>
<!-- Copyright 1998-2025 by Northwoods Software Corporation. -->
<meta name="description" content="A TextBlock with a scrollbar">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
<textarea id="mySavedModel" style="width:100%;height:250px"></textarea>
<script src="https://cdn.jsdelivr.net/npm/gojs/release/go-debug.js"></script>
<!-- The "ScrollingTable" extension, defining "AutoRepeatButton", will not be needed in v3.1,
where "AutoRepeatButton" will be predefined/built-in the library -->
<script src="https://cdn.jsdelivr.net/npm/create-gojs-kit/dist/extensions/ScrollingTable.js"></script>
<script id="code">
// Create a scrolling text Panel, containing a TextBlock whose name is given as the optional first argument.
// If not given the name defaults to "TEXT".
// Example use:
// go.GraphObject.build("ScrollingText", { . . . }, "TEXT")
// .bind("TEXT.alignment", "someNumberProperty")
// . . .)
// Note that if you have more than one of these in a Part,
// you'll want to make sure each one has a unique name.
go.GraphObject.defineBuilder("ScrollingText", args => {
const textname = go.GraphObject.takeBuilderArgument(args, "TEXT");
// an internal helper function for actually performing a scrolling operation
function incrTextOffset(obj, i) {
const diagram = obj.diagram;
if (!diagram) return;
const textblock = obj.panel.panel.panel.findObject(textname);
if (!textblock) return;
const normalHeight = textblock.metrics.arrText.length * textblock.metrics.fontHeight;
let offy = -textblock.alignment.offsetY + i;
if (offy < 0) offy = 0;
else if (offy >= normalHeight - 16) offy = normalHeight - 16;
if (-textblock.alignment.offsetY !== offy) {
diagram.commit(d => {
textblock.alignment = new go.Spot(0, 0, 0, -offy);
updateScrollBar(textblock);
}, null); // skipsUndoManager
}
}
function updateScrollBar(textblock) {
const bar = textblock.panel.elt(1); // the scrollbar is a sibling of the textblock
if (!bar) return;
const normalHeight = textblock.metrics.arrText.length * textblock.metrics.fontHeight;
const offy = -textblock.alignment.offsetY;
const up = bar.findObject("UP");
if (up) up.opacity = (offy > 0) ? 1.0 : 0.3;
const down = bar.findObject("DOWN");
if (down) down.opacity = (offy < normalHeight - 10) ? 1.0 : 0.3;
const needed = offy > 0 || normalHeight > textblock.panel.actualBounds.height;
bar.opacity = needed ? 1.0 : 0.0;
}
return new go.Panel("Table")
.addColumnDefinition(1, { sizing: go.Sizing.None })
.add(
new go.TextBlock({
name: textname,
column: 0,
stretch: go.Stretch.Fill,
margin: 1
}),
// this is the scrollbar
new go.Panel("Table", { column: 1, stretch: go.Stretch.Vertical, background: "#DDDDDD" })
.add(
// the scroll up button
go.GraphObject.build("AutoRepeatButton", {
name: "UP",
row: 0,
alignment: go.Spot.Top,
click: (e, obj) => incrTextOffset(obj, -16),
opacity: 0.3
})
.add(
new go.Shape("TriangleUp",
{ stroke: null, desiredSize: new go.Size(6, 6) })
),
// (someday implement a thumb here and support dragging to scroll)
// the scroll down button
go.GraphObject.build("AutoRepeatButton", {
name: "DOWN",
row: 2,
alignment: go.Spot.Bottom,
click: (e, obj) => incrTextOffset(obj, +16)
})
.add(
new go.Shape("TriangleDown",
{ stroke: null, desiredSize: new go.Size(6, 6) })
)
)
);
});
myDiagram =
new go.Diagram("myDiagramDiv", {
"undoManager.isEnabled": true,
"ModelChanged": e => { // just for demonstration purposes,
if (e.isTransactionFinished) { // show the model data in the page's TextArea
document.getElementById("mySavedModel").textContent = e.model.toJson();
}
}
});
myDiagram.nodeTemplate =
new go.Node("Auto", {
width: 100, height: 40, // initial size
resizable: true,
toolTip:
go.GraphObject.build("ToolTip", { width: 200, height: 100 })
.add(
go.GraphObject.build("ScrollingText", { stretch: go.Stretch.Fill })
// bind to the same string
.bindTwoWay("TEXT.text", "text")
// reset to zero scroll offset
.bind("TEXT.alignment", "text", t => go.Spot.TopLeft)
)
})
// remember any resizing done by the user
.bindTwoWay("desiredSize", "size", go.Size.parse, go.Size.stringify)
.add(
new go.Shape({ fill: "white" })
.bind("fill", "color"),
go.GraphObject.build("ScrollingText", {
stretch: go.Stretch.Fill,
"TEXT.editable": true // allow the user to edit the text in-place
})
// the TextBlock's name defaults to "TEXT"
.bindTwoWay("TEXT.text", "text")
);
myDiagram.model = new go.GraphLinksModel(
[
{ key: 1, text: "Alpha", color: "lightblue" },
{ key: 2, text: "Beta\n2\n3\n4\n5\n6\n7\n8\n9\n10", color: "orange" },
{ key: 3, text: "GammaGammaGammaGamma\nGamma2Gamma2Gamma2Gamma2\nGamma3Gamma3Gamma3Gamma3\nGamma4Gamma4Gamma4Gamma4", color: "lightgreen" },
{ key: 4, text: "qwerty\nad\nfsa\nsdfasd\nfasdsasdffgsdfgsdg\nfasd\nfads\nfdasf", color: "pink" }
],
[
{ from: 1, to: 2 },
{ from: 1, to: 3 },
{ from: 3, to: 4 },
{ from: 4, to: 1 }
]);
</script>
</body>
</html>