Restrict click event from propagating to parent

Hi,

I have a node shape template with the click event. And I have a multiple child shape template one inside the other and on the 4th level child I have one more click event. When I try to click this 4th child click event, the parent most click is also triggered which I need to stop.

I want only the child click event to be trigged when I click on the inner child shape template (A three dot button as shown in screenshot) and want to suppress the Parent click event.
Screenshot 2024-07-11 at 8.04.23 PM

Can I please get some help on this.

Thanks.

If I understand you correctly, in your click event handler you just need to set InputEvent.handled to true.

Yeah I have tried that but somehow it is not working for me Walter. I have set e.handled = true at child shape template but as my debugger comes to parent click function, e.handled is still false.

And FYI, my child shape template is a context menu, and click is to open the context menu options. And my parent shape template is a node shape template which also have a click event which adds the node to canvas from Palette on click.

And also I am facing this issue only while using the mouse click. When I try the same using accessibility (with keyboard) it works fine without any issues.

OK, this is confusing to me. Could you please post the outlines of your relevant templates, and the implementation of your click event handlers? You can skip the details of Panels and other GraphObjects in the visual tree that are not involved with this issue.

The outline is something as below Walter,

myDiagram.nodeTemplate =
    $(go.Node, "Spot",
        {
            click: function (e, node) {
                //Click on parent node template
            }
        },
        $(go.Shape, "Horizontal",
            new go.Binding("fill", "color")),
        $(go.TextBlock, "Switch Node",
            new go.Binding("text", "key")),
        $(go.Panel, "Horizontal",
            $(go.Shape, "Auto",
                { fill: "lightgoldenrodyellow" }),
            $(go.TextBlock, "Context Menu",
                { margin: 4 }),
            {
                click: function (e, node) {
                    //Inner most child click event
                }
            }
        )
    );

While debugging I found that the click event on the parent shape template is getting triggered from within the goJS showContextMenu method. In call stack I could see that the event is coming from standardMouseClick event.

The parent click is getting called even before completing the click event of the child. Below is the screenshot of stack trace

from screenshot : VM20195 is my child click event and VM20131 is my parent click event.

We already have these below props set for contextMenuTool

‘contextMenuTool.canStart’: function() {
if (this.diagram.lastInput.clickCount > 1) return false;
return go.ContextMenuTool.prototype.canStart.call(this);
},
‘contextMenuTool.standardMouseClick’: function() {
return false;
},

First, when I try your node template, I get the errors that “Horizontal” and “Auto” are undefined Shape.figure values. I hope you have defined those figures in your app.

Second, when I define your click event handlers and add a setting of InputEvent.handled, I find that a click on the nested panel does not result in calling the node’s click event handler:

myDiagram.nodeTemplate =
  $(go.Node, "Spot",
    {
      click: function (e, node) {
        console.log("node " + node.key);
      }
    },
    $(go.Shape, //"Horizontal",
      new go.Binding("fill", "color")),
    $(go.TextBlock, "Switch Node",
      new go.Binding("text", "key")),
    $(go.Panel, "Horizontal",
      $(go.Shape, //"Auto",
        { fill: "lightgoldenrodyellow" }),
      $(go.TextBlock, "Context Menu",
        { margin: 4 }),
      {
        click: function (e, panel) {
          e.handled = true;
          console.log("panel");
        }
      }
    )
  );

So the event bubbling is working correctly, with the bubbling stopped when the event handler sets InputEvent.handled to true.

Adding those overrides to ContextMenuTool.canStart and standardMouseClick has no effect because left click events do not normally invoke the ContextMenuTool. (I think your override of canStart doesn’t change any behavior anyway.) So that makes me think that your button click event handler is calling CommandHandler.showContextMenu. So I try:

myDiagram.nodeTemplate =
  $(go.Node, "Spot",
    {
      click: function (e, node) {
        console.log("node " + node.key);
      },
      contextMenu:
        $("ContextMenu",
          $("ContextMenuButton",
            $(go.TextBlock, "Context Menu for Node")
          )
        )
    },
    $(go.Shape, //"Horizontal",
      new go.Binding("fill", "color")),
    $(go.TextBlock, "Switch Node",
      new go.Binding("text", "key")),
    $(go.Panel, "Horizontal",
      $(go.Shape, //"Auto",
        { fill: "lightgoldenrodyellow" }),
      $(go.TextBlock, "Context Menu",
        { margin: 4 }),
      {
        click: function (e, panel) {
          e.handled = true;
          console.log("panel");
          e.diagram.commandHandler.showContextMenu(panel);
        },
        contextMenu:
          $("ContextMenu",
            $("ContextMenuButton",
              $(go.TextBlock, "Context Menu for Panel")
            )
          )
      }
    )
  );

Everything still works exactly as they are supposed to work. A click on the nested panel only calls its click event handler and not the node’s click event handler, and it shows the panel’s contextMenu because that was what was passed to the CommandHandler.showContextMenu method.

A right click on the panel shows the panel’s contextMenu, and a right click on some object other than the nested panel shows the node’s contextMenu. This is still with both of those ContextMenuTool overrides defined when initializing the Diagram.

So I am still confused – everything is working correctly. How is your situation different?

Thanks Walter for the detailed investigation. There seems to be some mishap. Let me clarify my issue clearly once again.

I defined all the shapes fine in my app. No issue with that.

I have a node shape template (parent most) with a click event and a child shape template (inner most) which is a context menu shape template with another click event.

And in this context menu shape template I am calling this : e.diagram.commandHandler.showContextMenu(menuButton.part);

So when I click this context menu button, the parent shape template click event is triggered TWICE. One with propagation and the another anonymously.

Then I have added e.handled = true in my context menu shape template which fixed the propagation issue. And now my parent click event is still called one more time. I want to stop this as well.

On debugging, (screenshot shared above), from call stack I can see that this parent click event is getting triggered from within the showContextMenu method with the help of standardMouseClick event.

From goJS documentation I have learned that there are some set of mouse click events that will be triggered from within the showContextMenu method : Tool | GoJS API

But there is no information on how to stop this click which eventually calls my parent shape template click event.

Hope I am bit clearer this time. Sorry once again for the confusion caused.

That’s a bit clearer, thank you, but I’m still confused. With the code that I most recently posted above, there is no way to invoke the Node.click event handler unless I click on some GraphObject that is not the nested panel or inside that nested panel.

Neither a left click nor a right click on the nested panel ever invokes that Node.click event handler. A right click on any object in the Node will not invoke that event handler.

Could you please post a node template that allows me to reproduce the problem?

myDiagram.nodeTemplate = $(go.Node, "Vertical",
        {
            click: function(e, node) {
                dispatch('someAction', node.data);
            }
        },
        // Rectangle Shape Template
        $(go.Shape, "Rectangle",
            {
                width: 100, height: 100,
                fill: "lightblue"
            }
        ),
        // Action Node Body Template
        $(go.Panel, "Vertical",
            // Icon Shape Template
            $(go.Picture,
                {
                    source: "path/to/your/icon.svg",
                    width: 32, height: 32
                }
            ),
            // Action Node Panel Template
            $(go.Panel, "Vertical",
                // Action Node Name Panel Template
                $(go.TextBlock,
                    {
                        margin: 5,
                        text: "Action Node Name",
                        stroke: "black",
                        font: "bold 12pt sans-serif"
                    }
                ),
                // Context Menu Shape Template
                $(go.Shape, "Circle",
                    {
                        width: 16, height: 16,
                        fill: "red",
                        click: function getValue(go, dispatch, $) {
                            return function(e, menuButton) {
                                e.diagram.commandHandler.showContextMenu(menuButton.part);
                                e.handled = true;
                            };
                        }
                    }
                )
            )
        )
    );

This seems odd:

This GraphObject.click event handler has no side-effect. Instead did you mean?

click: (e, menuButton) => {
  e.handled = true;
  e.diagram.commandHandler.showContextMenu(menuButton);
}

Yes Walter, that code eventually would be broken down into :


click: (e, menuButton) => {
  e.diagram.commandHandler.showContextMenu(menuButton.part);
  e.handled = true;
}

OK, I think we’ve fixed this bug that happens when CommandHandler.showContextMenu is called during a click event handler.

The fix will be in v3.0.8, which should be coming out soon this week.

Thanks for reporting the problem.

Thanks Walter. We are currently on v2.3.5 and it might take some time for us to upgrade. Any work around for this until then ?

Can you update to 2.3.*?

We are already on v2.3.5 Walter. Any specific version that we need to update to?

I was asking if you could update to 2.3.18 if it existed, or if you have tried updating to 2.3.17 right now.

I think this might work. It’s an ugly hack, but you can try it.

Add this override of ContextMenuTool.standardMouseClick:

// needed before v3.0.8
var myFlag = false;
myDiagram.toolManager.contextMenuTool.standardMouseClick = function(navig, pred) {
  const e = this.diagram.lastInput;
  if (myFlag && !this.isActive) {
    myFlag = false;
    const fake = e.copy();
    fake.event = null;
    fake.button = 2;
    fake.buttons = 2;
    this.diagram.lastInput = fake;
  }
  return go.ContextMenuTool.prototype.standardMouseClick.call(this, navig, pred);
};
// END OF needed before v3.0.8

Then in your “Circle” Shape click event handler:

            click: function (e, menuButton) {
                myFlag = true;  // needed before v3.0.8
                e.handled = true;
                e.diagram.commandHandler.showContextMenu(menuButton)
              }