How to get Exact location of a point in gojs

I am creating a diagram, where on hover on the middle of a link in between 2 nodes a plus sign will visible and when user click on that plus sign a div will pop up. So for that I want the div should exactly start from the plus sign but when I am changing the size of the window the div is getting placed in different positions. So how can I place the div at the plus sign which doesn’t change on different screen resolution

Code:-
‘’’
myDiagram.linkTemplate = $(
go.Link,
{
routing: go.Link.AvoidsNodes,
// routing: go.Link.Orthogonal,
corner: 5,
relinkableFrom: true,
relinkableTo: true,
reshapable: true,
resegmentable: true,
isLayoutPositioned: true
},
$(go.Shape),
$(
go.Panel,
“Auto”,
{
mouseEnter: function (e, obj) {
obj.findObject(“PlusSign”).visible = true;
obj.findObject(“Rectangle”).fill = “white”;
},
mouseLeave: function (e, obj) {
obj.findObject(“PlusSign”).visible = false;
obj.findObject(“Rectangle”).fill = “transparent”;
},
click: function (e, obj) {

           currentNodeLinkData = obj.part.data
           var plusSign = obj.findObject("PlusSign");
           var panelPoint = plusSign.getDocumentPoint(go.Spot.TopLeft);
           var diagram = myDiagram;
           var viewPoint = diagram.transformDocToView(panelPoint);
           var contextMenu = document.getElementById("contextMenu");


           contextMenu.style.backgroundColor = "white";
           contextMenu.style.display = "block";

           contextMenu.style.top = viewPoint.y + 140 + 'px'
           contextMenu.style.left = viewPoint.x + 130 + 'px'
           menu = true

           console.log("open")
           // Prevent link selection
           e.diagram.clearSelection();
           e.diagram.currentTool.stopTool();
        }
     },
     $(
        go.Shape,
        "Rectangle",
        {
           name: "Rectangle",
           fill: "transparent",
           strokeWidth: 0,
           desiredSize: new go.Size(20, 20),
           cursor: "pointer"
        },
        new go.Binding("fill", "color")
     ),
     $(
        go.Shape,
        "PlusLine",
        {
           name: "PlusSign",
           desiredSize: new go.Size(10, 10),
           stroke: "black",
           visible: false
        }
     )
  ),
  $(go.Shape, { toArrow: "OpenTriangle" })

);
‘’’


There two coordinate systems that matter: document and viewport. Everything is discussed at: GoJS Coordinate Systems-- Northwoods Software Normally a GraphObject will stay at the same document coordinates point even when the diagram is scrolled/panned/zoomed. But its point in viewport coordinates will likely change as the diagram is scrolled/panned/zoomed.

Normally some object in viewport coordinates will not change its view point when the Div gets a little bigger or smaller, but I suppose it depends on whether you have set Diagram.autoScale or Diagram.contentAlignment or have implemented some code that changes the Diagram.position.

The general InputEvent class has two relevant properties: documentPoint and viewPoint, which are in document coordinates and viewport coordinates, respectively. GoJS Events -- Northwoods Software So if you get an InputEvent you can get its point in document coordinates or viewport coordinates very easily.

If you have gotten the position of a GraphObject by calling GraphObject.getDocumentPoint, you can convert that to viewport coordinates by calling Diagram.convertDocToView. Depending on how you are positioning that Div, that may be sufficient, or else you may need to do a further conversion to page or screen coordinates, which is completely independent of the GoJS Diagram.

Hi thanks for your response. I am very much new to gojs and there are not proper examples available for all the functionalities in documentation. But as you written above first I am retrieving the document point and then from that I am getting the view point.

But the problem I am facing here is by using the view point I am not getting the exact point of the plus sign(provided the image1 for this). so for that I am adding some pixels to the view point. And after adding some pixels I am getting the exact point of plus sign(provided image2 for this).

And another problem I am getting the viewpoint is changing on different screen size. So because of this the pop up div is not displaying exactly on the plus sign on every screen.

for your better understanding I have attached the html and javascript code below

HTML code:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" integrity="sha512-iecdLmaskl7CVkqkXNQ/ZH/XLlvWZOJyj7Yy7tcenmpD1ypASozpmT/E0iPtmFIB46ZmdtAc9eNBvH0H/ZpiBw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
      <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
      <link rel="stylesheet" href="style.css">
  
  <!-- <script src="https://code.jquery.com/jquery-3.7.0.js" integrity="sha256-JlqSTELeR4TLqP0OG9dxM7yDPqX1ox/HfgiSLBj8+kM=" crossorigin="anonymous"></script> -->
  <script src="https://unpkg.com/gojs/release/go-debug.js"></script>
  <script src="index.js"></script>
</head>
<body>
 
 <div class="container">
    <div class="row">

       <div class="sidebar "  id="mySidebar"  > 
       <a href="javascript:void(0)" class="closebtn"
            onclick="closeNav()">× 
        </a>

        <div id="sidePalette" style="height: 420px; width:150px;  margin-top: 40px;" > </div>
        </div>

        <button class="openbtn" onclick="openNav()">Toolbox
        </button>

        <div>
      <div class=""  id="myDiagramDiv" style="height:700px; background-color: #DAE4E4; "> 
       
      </div>
      
      </div>
    
    </div>
    
    </div>

     <div id="contextMenu"  >
    <!-- ... context menu HTML -->
   
       </div>

   
  
  
  
</body>
</html>

javascript code:

var menu = false
var currentNodeLinkData = null;

function openNav() {
   var sideBar = document.getElementById("mySidebar");
   sideBar.style.backgroundColor = "white";
   sideBar.style.display = "block";
}

function closeNav() {
   var sideBar = document.getElementById("mySidebar");
   sideBar.style.display = "none";
}

const getUniqueKey = () => {
   var model = myDiagram.model;
   var maxKey = 0;
   // Find the maximum key among existing nodes
   model.nodeDataArray.forEach(function (nodeData) {
      if (nodeData.key > maxKey) {
         maxKey = nodeData.key;
      }
   });
   // Generate a new unique key by incrementing the maximum key
   var newKey = maxKey + 1;
   return newKey;
}

function init() {
   const $ = go.GraphObject.make;
   var colors = {
      blue: "#2a6dc0",
      orange: "#ea2857",
      green: "#1cc1bc",
      gray: "#5b5b5b",
      white: "#F5F5F5",
      yellow: "#FFFF00",
      darkorange: "#ff8c00",
      hotpink: "#ff69b4",
      red: "#FF0000",
      darkturquoise: "#00ced1"
   };

   myDiagram = new go.Diagram("myDiagramDiv", {
      "undoManager.isEnabled": true,
      // layout: $(go.LayeredDigraphLayout, { direction: 0 })
      layout: $(go.TreeLayout, { arrangement: go.TreeLayout.ArrangementHorizontal})
   });

   myDiagram.scrollMode = go.Diagram.InfiniteScroll;
   myDiagram.allowVerticalScroll = false;
   myDiagram.allowHorizontalScroll = false;

   myDiagram.nodeTemplate = $(go.Node, "Auto",
  new go.Binding("location", "loc", go.Point.parse),
      $(go.Panel, "Vertical",
         $(go.Panel, "Spot",
            $(go.Shape, "Circle",
               {
                  fill: "lightcoral", strokeWidth: 0, width: 45, height: 45,
                  portId: "",
                  fromLinkable: true,
                  fromSpot: go.Spot.AllSides,
                  toLinkable: true,
                  toSpot: go.Spot.AllSides,
                  cursor: "pointer",
               },

               new go.Binding("figure", "shape"), // Bind the figure property to the 'shape' data property
               new go.Binding("fill", "color")),
               new go.Binding("location", "loc", go.Point.parse),

            $(go.TextBlock,
               { margin: 5, font: "14px FontAwesome", alignment: go.Spot.Center, stroke: "white" },
               new go.Binding("text", "icon")),
         ),

         $(go.TextBlock,
            { margin: 5, font: "bold 9pt sans-serif", alignment: go.Spot.Bottom, textAlign: "center" },
            new go.Binding("text", "name"))
      )
   );

   myDiagram.linkTemplate = $(
      go.Link,
      {
         routing: go.Link.AvoidsNodes,
         // routing: go.Link.Orthogonal,
         corner: 5,
         relinkableFrom: true,
         relinkableTo: true,
         reshapable: true,
         resegmentable: true,
         isLayoutPositioned: true
      },
      $(go.Shape),
      $(
         go.Panel,
         "Auto",
         {
            mouseEnter: function (e, obj) {
               obj.findObject("PlusSign").visible = true;
               obj.findObject("Rectangle").fill = "white";
            },
            mouseLeave: function (e, obj) {
               obj.findObject("PlusSign").visible = false;
               obj.findObject("Rectangle").fill = "transparent";
            },
            click: function (e, obj) {

               currentNodeLinkData = obj.part.data
               var plusSign = obj.findObject("PlusSign");
               var panelPoint = plusSign.getDocumentPoint(go.Spot.TopLeft);
               
                console.log("Doc Point x: "+ panelPoint.x + " view point y: "+ panelPoint.y)
               var viewPoint = myDiagram.transformDocToView(panelPoint);
               var contextMenu = document.getElementById("contextMenu");

               contextMenu.style.backgroundColor = "white";
               contextMenu.style.display = "block";
               
               // contextMenu.style.top = viewPoint.y  + 'px'
               // contextMenu.style.left = viewPoint.x  + 'px'
               contextMenu.style.top = viewPoint.y + 140 + 'px'
               contextMenu.style.left = viewPoint.x + 130 + 'px'
               menu = true

               console.log("open")
               console.log("View Point x: "+ viewPoint.x + " view point y: "+ viewPoint.y)
               // Prevent link selection
               e.diagram.clearSelection();
               e.diagram.currentTool.stopTool();
            }
         },
         $(
            go.Shape,
            "Rectangle",
            {
               name: "Rectangle",
               fill: "transparent",
               strokeWidth: 0,
               desiredSize: new go.Size(20, 20),
               cursor: "pointer"
            },
            new go.Binding("fill", "color")
         ),
         $(
            go.Shape,
            "PlusLine",
            {
               name: "PlusSign",
               desiredSize: new go.Size(10, 10),
               stroke: "black",
               visible: false
            }
         )
      ),
      $(go.Shape, { toArrow: "OpenTriangle" })
   );

   var model = new go.GraphLinksModel(
      [
         { key: 1,  shape: "Square", color: colors["darkorange"], name: "Loop", icon: "\uf01e" },
         { key: 2, shape: "Circle", color: colors["hotpink"], name: "Update", icon: "\uf044" },
         // { key: 3, shape: "Square", color: colors["hotpink"], name: "Get", icon: "\uf002" },
         // { key: 4, shape: "Square", color: colors["blue"], name: "Screen\nRecorder", icon: "\uf390" },
         // { key: 5, shape: "Circle", color: colors["red"], name: "Stop", icon: "\uf04d" },
         // { key: 6, shape: "Circle", color: colors["darkturquoise"], name: "Start", icon: "\uf04b" },
         // { key: 7, shape: "Diamond", color: colors["darkorange"], name: " is there an\nAccount id?", icon: "\uf53e" },
         // { key: 8, shape: "Diamond", color: colors["darkorange"], name: " Recieved\nAccount id?", icon: "\uf53e" },
         // { key: 9, shape: "Diamond", color: colors["darkorange"], name: " Double check\n id?", icon: "\uf53e" },
         // { key: 10, shape: "Square", color: colors["darkorange"], name: " Assign\n inputAccountID", icon: "\uf53e" },
         // { key: 11, shape: "Diamond", color: colors["darkorange"], name: " set billing city\n to SF", icon: "\uf53e" },
         // { key: 12, shape: "Square", color: colors["hotpink"], name: "Lookup\n Account id", icon: "\uf002" },
         // { key: 13, shape: "Square", color: colors["blue"], name: "Get Account id", icon: "\uf390" },

      ],
      [
         // { from: 1, to: 2 },
         // // { from: 1, to: 3 },
         // { from: 2, to: 3 },
         // { from: 3, to: 4 },
         // // { from: 3, to: 6 },
         // // { from: 4, to: 7 },
         // { from: 3, to: 5 }
      ]
   );

   myDiagram.model = model;

   // initialize the Palette that is on the left side of the page
   myPalette = new go.Palette("sidePalette", {
      layout: $(go.TreeLayout),
      // draggingTool: null
   });

   myPalette.nodeTemplate = $(go.Node, "Auto",
     
      $(go.Panel, "Horizontal",
         $(go.Panel, "Spot",
            $(go.Shape, "Circle",
               {
                  fill: "lightcoral", strokeWidth: 0, width: 25, height: 25,
                  portId: "",
                  fromLinkable: true,
                  fromSpot: go.Spot.AllSides,
                  toLinkable: true,
                  toSpot: go.Spot.AllSides,
                  cursor: "pointer",
               },
               new go.Binding("figure", "shape"), // Bind the figure property to the 'shape' data property
               new go.Binding("fill", "color")),
            $(go.TextBlock,
               { margin: 5, font: "14px FontAwesome", alignment: go.Spot.Center, stroke: "white" },
               new go.Binding("text", "icon")),
         ),
         $(go.TextBlock,
            { margin: 5, font: "bold 9pt sans-serif", alignment: go.Spot.Bottom },
            new go.Binding("text", "name"))
      )
   );

   const addNewNode = (data) => {
      // console.log(data)
      var model = myDiagram.model;
      const newKey = getUniqueKey()
      // For instance we are taking first link and removing so that we can add newLink
      model.startTransaction("remove-link");
      model.removeLinkData(currentNodeLinkData);
      model.commitTransaction("remove-link");

      // Add new node
      model.startTransaction("addNode");

      const newNodeData = {
         key: newKey,
         shape: data.shape,
         color: data.color,
         name: data.name,
         icon: data.icon,
      };

      model.addNodeData(newNodeData);

      model.addLinkData({
         from: currentNodeLinkData.from,
         to: newKey
      });

      model.addLinkData({
         from: newKey,
         to: currentNodeLinkData.to
      });

      model.commitTransaction("addNode");

      var contextMenu = document.getElementById("contextMenu");
      contextMenu.style.display = "none";

   };

   var paletteData = [
      { key: 3, shape: "Square", color: colors["hotpink"], name: "Look", icon: "\uf002" },
      { key: 2, shape: "Circle", color: colors["hotpink"], name: "Update", icon: "\uf044" },
      { key: 3, shape: "Square", color: colors["hotpink"], name: "Get", icon: "\uf002" },
      { key: 2, shape: "Circle", color: colors["hotpink"], name: "Delete", icon: "\uf044" },
      { key: 4, shape: "Square", color: colors["blue"], name: "       Screen ", icon: "\uf390" },
      { key: 7, shape: "Diamond", color: colors["darkorange"], name: "Deletion", icon: "\uf53e" },
      { key: 11, shape: "Diamond", color: colors["darkorange"], name: "Assignment", icon: "\uf53e" },
      { key: 1, shape: "Square", color: colors["darkorange"], name: "Loop", icon: "\uf01e" },
      { key: 6, shape: "Circle", color: colors["darkturquoise"], name: "Start", icon: "\uf04b" },
      { key: 5, shape: "Circle", color: colors["red"], name: "Stop", icon: "\uf04d" }
   ]

   myPalette.model = new go.GraphLinksModel(paletteData)

   contextMenu = new go.Palette("contextMenu", {
      layout: $(go.TreeLayout),
      // nodeTemplate: myPalette.nodeTemplate,
      model: new go.GraphLinksModel(paletteData),
      draggingTool: null
   });

   contextMenu.nodeTemplate = $(go.Node, "Auto",
      {
         click: function (e, obj) {
            console.log("palette clicked");
            var data = obj.part.data;
            console.log(data);
            addNewNode(data);
         }
      },
      $(go.Panel, "Horizontal",
         $(go.Panel, "Spot",
            $(go.Shape, "Circle",
               {
                  fill: "lightcoral", strokeWidth: 0, width: 25, height: 25,
                  portId: "",
                  fromLinkable: true,
                  fromSpot: go.Spot.AllSides,
                  toLinkable: true,
                  toSpot: go.Spot.AllSides,
                  cursor: "pointer",
               },
               new go.Binding("figure", "shape"), // Bind the figure property to the 'shape' data property
               new go.Binding("fill", "color")),
            $(go.TextBlock,
               { margin: 5, font: "14px FontAwesome", alignment: go.Spot.Center, stroke: "white" },
               new go.Binding("text", "icon")),
         ),
         $(go.TextBlock,
            { margin: 5, font: "bold 9pt sans-serif", alignment: go.Spot.Bottom },
            new go.Binding("text", "name"))
      )
   );

   document.addEventListener("click", function (event) {
      var contextMenu = document.getElementById("contextMenu");
      if (event.target !== contextMenu && !contextMenu.contains(event.target) && !menu) {
         contextMenu.style.display = "none";
         // console.log("closed")
      }
      menu = false
   });
}

window.addEventListener('DOMContentLoaded', init);


In trying your code, I hope you noticed the warning messages in the console saying how there is no “location” property on the “Spot” Panel that can be the target of a Binding. So you can just delete that useless Binding.

The values that you are printing out for the view point look correct to me.

Maybe you are looking for a sample such as Data Visualization GoJS Sample
That moves an HTML element to the InputElement.viewPoint, plus an offset so that the HTML element does not obscure what the user is looking at.