Links are shown on top of everything


we use one of the latest GoJS library versions (2.1.38) and face sometimes a very frustrating issue, which is I guess related to the z-index of GraphObjects in the diagram. Our users started to complain, that links, which connect nodes are shown incorrectly.

I was wondering why nodes are z-indexed correctly and links not, because if you look at the example, which I have attached below, you could see, that groups and nodes appear correctly in the diagram, the z-index depends on the node addition to the diagram - no strange overlapping issues, but the links are just stupidly wrong. In the attachment, you can see that links are showing on the top even from the far behind group.

As a user I would expect, that links are managed the same way as nodes are - only the relevant links are shown, for example, if I look at the top group, I only see the links, which are incoming and outgoing of this group and all the links, which are in this group…

We would like to improve this behavior, as it is really frustrating and users have a hard time understanding the connections between nodes, because you need to do a lot of additional work, like dragging the group somewhere to the side, just to not see the irrelevant links. What could be done in this case?

I will also attach the link template and diagram setup parameters (if you need any other input, please, let me know):

  • Link template:
    setupLinkTemplate() {
        return this.create(go.Link,
                routing: go.Link.Orthogonal, corner: 5,
                relinkableFrom: true, relinkableTo: true,
                curviness: 5, curve: go.Link.JumpOver
            new go.Binding("deletable"),
            new go.Binding("visible"),
            new go.Binding("opacity"),
            new go.Binding("pickable", "opacity", (o) => o > 0),
            new go.Binding("curve", "opacity", (o) => o > 0 ? go.Link.JumpOver : go.Link.Normal),
                    stroke: "gray"
                new go.Binding("strokeWidth", "", (obj) => {
                    if (this.linkPortsAreReadOnly(obj)) {
                        return 3;
                    } else {
                        return 2;
                { fill: "gray", toArrow: "Standard", stroke: "gray" },
  • Diagram setup:
    setupDiagram(params) {
        return this.create(go.Diagram, params.container, {
            initialContentAlignment: go.Spot.TopCenter,
            contentAlignment: go.Spot.TopCenter,
            layout: this.create(go.LayeredDigraphLayout,
                direction: 90,
                isOngoing: true,
                isRealtime: false
            "undoManager.isEnabled": false,
            "model": new go.GraphLinksModel(),
            "model.makeUniqueKeyFunction": this.generateUUID,
            "model.linkFromPortIdProperty": "frompid",
            "model.linkToPortIdProperty": "topid",
            "animationManager.isInitial": false,
            allowDrop: true,
            padding: 150

P.S: We have the support available as we recently renewed our license for the 2.1.* version.

Are you using layers at all? I.e., do you set or bind the Part.layerName property in any of your templates?

If you haven’t, then all of your Parts are going in the same Layer. Since Links are usually added after Nodes, it means that Links will appear in front of Nodes.

Each layer automatically changes the z-order of Groups so that the Group is behind all of its member Parts. However, that means that it does not try to automatically change the z-order of regular Nodes or Links.

Here’s one possible solution. I hope it matches your expectations, but it might not because there are many plausible policies that one could implement.

Here’s a function that explicitly assigns the Part.zOrder property for all Nodes and Links in a Diagram, assuming that all Nodes and Links are in the same Layer:

  function zOrder(diag) {
    function zOrder1(part, z) {
      part.zOrder = z++;
      if (part instanceof go.Group) {
        part.memberParts.each(m => z = zOrder1(m, z));
      return z;
    diag.commit(diag => {
      var z = 0;
      diag.nodes.each(n => {
        if (n.isTopLevel) z = zOrder1(n, z);
      diag.links.each(l => {
        if (l.isTopLevel) z = zOrder1(l, z);

You might want to call it after you load the model:

      $(go.Diagram, . . .,
          "InitialLayoutCompleted": e => zOrder(e.diagram),
          "ChangedSelection": e => zOrder(e.diagram),

In order to make groups more clearly visible, I suggest that when they are selected they are moved into the “Foreground” Layer.

            $(go.Group, . . .,
                selectionChanged: group => {
                  var lay = group.isSelected ? "Foreground" : "";
                  group.layerName = lay;
                  group.findSubGraphParts().each(m => m.layerName = lay);
              . . .

Thanks for reply,

no, we didn’t used any Layers apart from the default one. I’ve tried out proposed solution and it looks that only bringing the Group to the foreground was enough. I additionally included all external Links into the collection, which is moved to the foreground Layer.

Right now only one problem is left - Links are still jumping over Links, that are in the background and cannot be seen.

Is there a way to prevent this?

Does that zOrder function help?

Not really, some of the links were still appearing on top after you deselect everything, also the link jump overs were still happening.

Yes, the computation of the jump-overs (or jump-gaps) cannot take into account whether there is some opaque background between the link path and the one that it is jumping over.

Maybe you don’t need to show jump-overs at all.