Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tap fired twice in Chrome when using touch on Windows 8.1 / 10 #917

Closed
LeJared opened this issue Feb 9, 2016 · 8 comments
Closed

Tap fired twice in Chrome when using touch on Windows 8.1 / 10 #917

LeJared opened this issue Feb 9, 2016 · 8 comments

Comments

@LeJared
Copy link

LeJared commented Feb 9, 2016

When using Chrome with a touch screen on Windows 8.1 or Windows 10, tap gets fired twice.
First tap is fired on touchend and second on (emulated) mouseup.

Working Example:
http://codepen.io/LeJared/pen/ZQmrZK?editors=1011
(Watch Console)

I guess the mouseup should be ignored when tap was initiated by touchstart?

@kara
Copy link
Contributor

kara commented Feb 19, 2016

Yeah, I'm getting the same behavior on Chrome 47 on Surface Pro. Works in Firefox 43 and IE 11, just not Chrome.

@arschmitz
Copy link
Contributor

So all three of these cases take different code paths. Chrome on a touch screen uses the chrome / w3c flavor of touch-events, firefox does not support touch so your actually getting just mouse events( unless you enabled the flag in firefox yourself to turn them on ) and IE11 is using pointer events. this does sound like a bug where we need to suppress the mouse event after a touch. @RByers any thoughts?

@BillBooks
Copy link

I think that the root cause on Chrome s the same as for #863

@kara
Copy link
Contributor

kara commented Mar 7, 2016

I did some digging, and it looks like TouchMouseInput already tries to suppress mouse events fired after touch here.

// when we're in a touch event, so  block all upcoming mouse events
// most mobile browser also emit mouseevents, right after touchstart
if (isTouch) {
   this.mouse.allow = false;
} else if (isMouse && !this.mouse.allow) {
   return;
}

// reset the allowMouse when we're done
if (inputEvent & (INPUT_END | INPUT_CANCEL)) {
   this.mouse.allow = true;
}

It seems like the problem might be that this.mouse.allow is reset to true before the mouse events even fire, so the mouse event handlers are always allowed to run (and you get two taps). Specifically:

  • "touchstart" fires and sets this.mouse.allow = false.
  • "touchend" fires and because it's an INPUT_END event, it resets this.mouse.allow = true.
  • "mousedown" fires and this.mouse.allow is true, so handler runs.
  • "mouseup" fires and this.mouse.allow is still true, so handler runs.

I was able to fix the problem (naively) on my Surface by adding an isMouse check before resetting.

@arschmitz Am I understanding this correctly?

Edit: looks like @BillBooks already came to a similar conclusion a few weeks ago (#863). Just read his comment/issue thread now. I put in a PR for a quick patch, but closed it pending a larger rewrite.

@RByers
Copy link

RByers commented Mar 9, 2016

Sorry for the delay. Chrome on Windows should be the same as Chrome on Android and Safari here (except that it's much more likely that a user will have a real mouse on Windows in addition to a touchscreen, but of course that can be the case on Android too so hopefully hammer isn't relying on the UserAgent string to try to guess whether a real mouse might be present).

We (finally) shipped an API to make scenarios like this easier in Chrome: MouseEvent.sourceCapabilites.firesTouchEvents. I'd suggest you use it to identify/suppress redundant mouse events when it's available.

@BillBooks
Copy link

We had a bunch of other requirements --- in particular, we needed to handle some mouse up events synchronously so that they didn't fall afoul of pop up blockers, and for our application the mod 2 behavior of doubletap recognition was sub-optimal. We ended up watching for tap events, and ignored events with a tapCount > 1 that had a different pointerType from the previous tap. There are probably scenarios where this over ignores, but we judged the solution good enough. Unignored taps were handled synchronously if need be, and otherwise we fell into logic that more or less duplicated the tap handler. While having to copy the code to delay double tap recognition was unfortunate, the overall complexity of our code was improved with only one tap handler.

[Oh, and the simple expedient of just callingpreventDefault wasn't an option for us, and, in any case, it doesn't really help unless your Hammer.js event handler runs while the browser event handler is still on the stack.]

@jtangelder
Copy link
Member

@RByers that is a great addition to the event, we should definitely add support to this at the lib. @kara mailed me a suggestion to implement a solution like PolymerGestures (https://github.com/Polymer/polymer-gestures/blob/master/src/mouse.js#L61), which looks like good fix for this issue. I think we can use that als a fallback function for the sourceCapabilities.firesTouchEvents property.

@RByers
Copy link

RByers commented Mar 19, 2016

Yeah, its not too hard to guess pretty reliably. My polyfill uses a timer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants