Contextmenu location

Apologies @BartButenaers @dceejay
It usually takes me a few minutes or hours to fully comprehend how things work around here, but it's unfortunately taken me 2 weeks to grasp this :shushing_face:
I think that the plan is to grab the coordinates from the button node, and feed those into the contextmenu node by way of Message based fields.
OK I get it!!!

This is working OK aligning the menu to the top right of the box, but I've found a minor issue when using an icon in the button node.
If I click anywhere in the box (other than the icon), the position is reported correctly, if however I click the icon itself, the coordinates reported change and are incorrect.


Clicking around the icon -

bbox: array[4]
0: 313
1: 179
2: 372
3: 120

Clicking the icon -

bbox: array[4]
0: 331
1: 195
2: 390
3: 136

The click event is tied to clicked element. The box can be measured from any known part of component. The overall question is - what is the meaningfull box the component should provide. There is one and straight rule as for button it may be whole button but for more complex elements there is many possibilities. I know for state-trail the box has no meaning at all. So it is pretty
hard to lay out rules that way.

@Paul-Reed I've pushed a better fix to master - give that a whirl.


Nice! works really well now, and the button is spitting out the same coordinates wherever the user clicks within the bounding box.

Thank you


In fact, this is @BartButenaers & @Steve-Mcl's contextmenu node, as used in my dashboard.
The dashboard size is deigned for my phone in landscape, and the menu works great there too!
The changes made by @dceejay have made it now that the menu popup always appears in the same location, regardless of the browser.
Also, really liking @hotNipi's artless-gauges, their simplicity seems to make data easier to absorb.


Hey @dceejay, @Paul-Reed,
Nice work! Thank you both for the assistance!
I really should add a simple example to the contextmenu node's readme page, to show the cooperation with the button widget (once the feature is on NPM).

1 Like

Just tweaking the position a little makes the menu appear in a more natural position relative to the button icon;




PS - As I use a common menu across all pages, I load the 'message based' menu from context - so I only have to edit it once to make global menu changes.


I may be reading this incorrectly, but the 'touchstart' event does not return msg.position or msg.coordinates.


Hi Daniel,
I see here that @dceejay has added it to the buttonClick event. But don't see any other button related event (e.g. touchstart) there. So expected the same behaviour...

Hi Bart,
Specifically I was interested in your SVG nodes output and should have said such in the message, but as it related to the 'contextmenu' I put it here. I don't know where to place the menu. I could just put it in the top corner but on a 7in screen that annoying.

Nice catch! I have created a new issue for the SVG node. Will need to refactor the coordinate related fields in the output message anyway in the 2.0.0 release, to use the same fields as the other UI nodes (as described above in this discussion). Will keep you updated!

@greenembrace for now, is there any reason you are using touchstart over click? Click should work on a touch screen & does contain coordintated.

@BartButenaers we need to handle this differently.

the touch events present their pageX and pageY coordinates in a different property to click.


//touch event
pt.x = evt.originalEvent.touches[0].pageX;
pt.y = evt.originalEvent.touches[0].pageY;

//click event
pt.x = evt.pageX;
pt.y = evt.pageY;

So we can fix touch events like this...

    initController: function($scope, events) {
        // Remark: all client-side functions should be added here!  
        // If added above, it will be server-side functions which are not available at the client-side ...

        // ADD THIS FUNCTION INSIDE initController....
        //helper function to get event coordinates
        function pointerEventToXY(e){
            if(e.type == 'touchstart' || e.type == 'touchmove' || e.type == 'touchend' || e.type == 'touchcancel'){
                var touches = e.touches || e.originalEvent && e.originalEvent.touches.touches;
                if(touches && touches.length){
                    var touch = touches[0] || changedTouches[0];
                    return {
                        x : touch.pageX,
                        y : touch.pageY
            } else if (e.type == 'click' || e.type == 'mousedown' || e.type == 'mouseup' || e.type == 'mousemove' || e.type == 'mouseover'|| e.type=='mouseout' || e.type=='mouseenter' || e.type=='mouseleave') {
                return {
                    x : e.pageX,
                    y : e.pageY
            return null;

        // Get the mouse coordinates (with origin at left top of the SVG drawing)
        let evtpos = pointerEventToXY(evt);
            var pt = $scope.svg.createSVGPoint();
            pt.x = evtpos.x;
            pt.y = evtpos.y;
            pt = pt.matrixTransform($scope.svg.getScreenCTM().inverse());
            //relative position on svg
            msg.coordinates = {
                x: pt.x,
                y: pt.y
            //absolute position on page - usefull for sending to popup menu
            msg.position = evtpos;

Steve, tried that this morning but (if I'm not mistaken) it didn't work on my Android phone. The touchstart worked fine.

Thanks for having a look at this!! I'm not at my pc at the moment, but we perhaps need to look at the fields that @dceejay has used for his button (and use the same + also send old fields for legacy flows?).

Don't need to support anything legacy as far as I can see Bart. It just didn't work for a touchscreen when using touchstart. TBH, most users would instinctively choose click.

And even if they did choose touch events, the odds are even smaller they would be using the context menu and odds reduce even further that they're using dynamic position.

Lastly, the worst that can happen? The context menus appear in the correct location.

Ps, I tried the above code & it works for me.

1 Like

Seems I had posted an incorrect link. This is the issue. For the "click" event we currently (npm version) send this output:


But the dashboard button:

Sends this when clicked:


Don't know if @hotNipi has also used the same convention meanwhile for his UI node(s)? But if we would use rename our fields, then we break existing flows.
Of course there is no issue when we add the bounding box, since that is a new output message field (which we can get via document.getElementById("myPath").getBBox(); ).

We could just add those to the click type events and if present in the context menu, use them.

No need for any legacy flags as far as I can see Bart.

1 Like

I haven't changed anything cos there wasn't clear agreement for unification.
State-trail still uses this:
But I will change if needed even if it break some flows. It is not that drastic.

1 Like

Perhaps we should align on including an event object in the msg with pageX and pageY at minimum?

MouseEvent.pageX Read only

The X coordinate of the mouse pointer relative to the whole document.

MouseEvent.pageY Read only

The Y coordinate of the mouse pointer relative to the whole document.

then if we have other coordinates, try to stick to mozilla standards (where it makes sense)

e.g. screenX, screenY, clientX, clientY, etc (and touch events)

1 Like

So this will be the state-trail output


1 Like