[SOLVED] Issue with click event on touch screen

I have a button that subscribes to the click event.
As soon as the click occurs I hide the button and I show a new one in the same position.
When I test this on desktop it works fine, but on mobile, it triggers the click event also on the second button.

Here is the repro:
https://playcanvas.com/project/536769/overview/ui-issues

Hit play and look in the console.
Click on the red button. It shows the blue button and prints “click 1”.
Then click on the blue button. It prints “click 2”.

On mobile, as soon as I touch the red button, it prints “click 1” and “click 2”.

Am I missing something? Or should I post an issue on github?

any thought? :blush:
I’ll add it to GitHub in the meantime.

Well, if you hold your touch for a second and then release - it doesn’t happen.
So problem with event, obviously.

Maybe when clicking on mobile, touch event trigger, and then click event trigger.

How about using this.entity.element.on('touchstart', callback) on mobile ?

But I enable the second button after the first click, so after I released the finger. So it should not receive any event.

Well, I guess problem with the way how it works.

Click detecting must be kinda raycasting, since our elements are entities.
So, raycast recognize your click on red button and even when you enable blue, on blue.

I’m not sure, but seems like problem within it.

Not an ideal solution, but I can fix your repro by deferring the enabling of the button until the next event phase.

TouchIssue.prototype.onClick1 = function() {
    this.button1.enabled = false;
    
    var self = this;
    setTimeout(function() {
        self.button2.enabled = true;
    }, 0);

    console.log('click 1');
};

TouchIssue.prototype.onClick2 = function() {
    this.button2.enabled = false;
    
    var self = this;
    setTimeout(function() {
        self.button1.enabled = true;
    }, 0);
    
    console.log('click 2');
};

I suspected the touch event was being propagated up to the mouse handler, so you were getting two clicks (a touch click and a mouse click). However, looking at the PlayCanvas code inside “element-input.js” it looks like it is invoking preventDefault, so I don’t have a complete explanation for you sorry.

n!

Yes I did the same trick, and as you say is not the best.
I hope @vaios or @dave could give a look too.
I have a sequence of pages showing up after clicking a button in each page and now I have to apply the setTimeout to several of buttons.

The problem is that when you handle touchstart and touchend then Chrome seems to fire mousedown and mouseup after the touchend event. You can try this yourself by doing something like this in any page:

document.body.addEventListener('touchstart', function () {console.log('touchstart', Date.now())})
document.body.addEventListener('touchend', function () {console.log('touchend', Date.now())})
document.body.addEventListener('mousedown', function () {console.log('mousedown', Date.now())})
document.body.addEventListener('mouseup', function () {console.log('mouseup', Date.now())})

Using the Developer tools in Chrome toggle the Device Toolbar and pick a mobile device, then refresh the page with the Developer tools open. Execute the code above in the Developer tools console and then try touching the page and release the touch after a while. You’ll see that mousedown is fired after the touch is released.

So in you case what happens is on touchend we fire the click event which disables the first button and enables the second button. Then mousedown is fired on the second button and then mouseup again on the second button which translates into a click event on the second button.

I’m not sure we can do something about that - handling it with a simple timeout in your code should fix it.

Thanks. I’ll give it a try. So if I’m understanding correctly chrome is trying to simulate mouse events on a touch device.
Wouldn’t be possible to force Playcanvas to ignore any mouse events in this case. By some settings?
This should resolve it.

No we need to handle mouse events as well as touch events because some devices support both.

Starting from your input I found that this is a common annoying issue.
So it seems that a possible solution could be to call event.preventDefault() on onTouchEnd event.
I tried in the repro and it works fine now :grinning:

in the initialize I subscribe to touch end event:

    // Only register touch events if the device supports touch
    var touch = this.app.touch;
    if (touch) {
        touch.on(pc.EVENT_TOUCHEND, this.onTouchEnd, this);
    }

then the function:

TouchIssue.prototype.onTouchEnd = function(evt) {
    evt.event.preventDefault();
};
1 Like