Position element on the link according to the cursor position

I’m showing remove/add actions when I hover the link.
Actions are shown in the middle of the link always.

Here is the code I use for actions panel that holds and position them:

            $GO(go.Panel, 'Horizontal', {
                name: 'ACTIONS',
                visible: constrained,
                background: 'transparent',
                alignment: go.Spot.Center,
                alignmentFocus: go.Spot.TopCenter,
                segmentFraction: 0.5,
                segmentOrientation: go.Orientation.Upright,
                segmentOffset: new go.Point(0, 7),
            },

Is there a way to show actions in the same place on the link where cursor is?

<!DOCTYPE html>
<html>
<head>
  <title>Buttons Shown on Hover over Link</title>
  <!-- Copyright 1998-2025 by Northwoods Software Corporation. -->
</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>
  <script id="code">
const myDiagram =
  new go.Diagram("myDiagramDiv", {
      hoverDelay: 200, // controls how long to wait motionless (msec) before showing Adornment
      "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();
        }
      }
    });

// the node template doesn't matter for this sample
myDiagram.nodeTemplate =
  new go.Node("Auto")
    .bind("location", "loc", go.Point.parse)
    .add(
      new go.Shape({
          fill: "white",
          portId: "", fromSpot: go.Spot.AllSides, toSpot: go.Spot.AllSides
        })
        .bind("fill", "color"),
      new go.TextBlock({ margin: 8 })
        .bind("text")
    );

// This is adapted from the samples/hoverButtons.html sample.

// This Adornment is shown by the mouseHover event handler:
const linkHoverAdornment = new go.Adornment('Horizontal', {
  locationSpot: go.Spot.Center,
  locationObjectName: "CENTER",
  // hide the Adornment when the mouse leaves it
  mouseLeave: (e, obj) => {
    var ad = obj.part;
    ad.adornedPart.removeAdornment('mouseHover');
  },
  background: "transparent",
  isActionable: true, // needed because this is in a temporary Layer
  click: (e, obj) => {
    var link = obj.part.adornedPart;
    link.diagram.select(link);
  }
})
  .add(
    go.GraphObject.build('Button', {
        alignment: go.Spot.Left, alignmentFocus: go.Spot.Right,
        click: (e, obj) => alert('Hi!')
      })
      .add(new go.TextBlock('Hi!')),
    new go.Shape({
      name: "CENTER",
      width: 12, height: 12,
      fill: null, strokeWidth: 0
    }),
    go.GraphObject.build('Button', {
        alignment: go.Spot.Right, alignmentFocus: go.Spot.Left,
        click: (e, obj) => alert('Bye')
      })
      .add(new go.TextBlock('Bye'))
  );

myDiagram.linkTemplate =
  new go.Link({
      routing: go.Routing.AvoidsNodes,
      // show the Adornment when a mouseHover event occurs
      mouseHover: (e, obj) => {
        var link = obj.part;
        linkHoverAdornment.adornedObject = link;
        linkHoverAdornment.location = e.documentPoint;
        link.addAdornment('mouseHover', linkHoverAdornment);
      }
    })
    .add(
      // a wide transparent background Shape makes it easier to interact with
      new go.Shape({ isPanelMain: true, stroke: "transparent", strokeWidth: 5 }),
      new go.Shape({ isPanelMain: true }),  // this is what users see
      new go.Shape({ toArrow: "OpenTriangle" }),
      new go.TextBlock({
          segmentOrientation: go.Orientation.Upright,
          background: "whitesmoke"
        })
        .bind("text")
    );

myDiagram.model = new go.GraphLinksModel(
[
  { key: 1, text: "Alpha", color: "lightblue", loc: "0 100" },
  { key: 2, text: "Beta", color: "orange", loc: "300 0" },
],
[
  { from: 1, to: 2, text: "label 1" },
]);
  </script>
</body>
</html>
1 Like

@walter Thank you, it works great.

In my case, however, the actions are part of the connection template and only become visible on hover.
Will specifying the location property work for a go.Panel in the same way it does for an adorner in this scenario?

No, not if it’s an element of a Panel – then the Panel’s PanelLayout’s rules govern the measurement and arrangement of its elements.

Besides, there’s no Panel.location or GraphObject.location property.

@walter thank you for the reply 👍