-
Notifications
You must be signed in to change notification settings - Fork 46.8k
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
onTouchTap triggering handler twice #2061
Comments
It's arguable for Since what's really going on here is the browser trying to play nice by emulating mouse events for touch, a way to selectively disable emulation is an idea. But node-wise sounds kind of useless (and becomes yet another reserved non-standard attribute) and I'm not sure how you would do it "owner component"-wise, it would probably introduce additional issues for when components are supplied as children to another component. PS. You can currently return |
Yeah, I can return |
It seems this is a wide-spread issue, some quick googling reveals that a lot of people seem quite confused about what to do, didn't find any useful answer during my quick search. Additionally, it doesn't seem to be |
Ok, so I fixed it for myself the hard way - if taps are enabled then don't react to clicks. And I check for taps like that:
That of course rules out laptops with touch screens, and is not working very good on IE10, but I can live with it for now. |
@piranha It also means you have to support touchevents for all components, you cannot fallback in places you don't care. |
Yeah, we're doing |
@piranha, I believe your solution isn't ideal. First off, the touch detection you're using gives false positives (including my current version of Chrome, 36.0.1985.143. Second, I'm not sure about the environment you're working with, but you're either listening for mouse or touch events. But how about devices that support both? When trying to fix this problem, we also tried to The solution we came up with is to ignore all mouse events within 750ms of the last touch event. Since all emulated mouse events are triggered directly after the touch events, we can easily ignore these. We chose 750ms because it's a reasonable amount of time to be considered as the same user interaction. With this solution the tap handler is always invoked once per user interaction. It also never excludes events, so the user is allowed to use both their finger and mouse. Our solution: zilverline/react-tap-event-plugin@zilverline:v0.11.0...patch-tap-event |
@s0meone Hmm, regarding 750ms, are there actually emulated mouse events that fire delayed from the touch action/does not have a touch action? I would assume that a delay of "0" would be enough (if it happens immediately it's from the touch event, else it's from a mouse). |
Wow, that's certainly much better, I just needed a really quick fix and couldn't come up with better idea. Your patch seems a good one, though I think it makes sense decreasing threshold to, say, 350ms. IIRC 300ms is a delay between a tap and an emulated click event. |
Well, you'd hope the emulated mouse events would be fired immediately after the touch events but they aren't. We've had delays up to ~450ms. Logs from the iOS Simulator: [Log] touch event topTouchStart 1409061289113 (react-with-addons.js, line 16870) [Log] touch event topTouchStart 1409061349552 (react-with-addons.js, line 16870) Also, lowering the threshold doesn't really make sense, who's going to switch between a finger and a mouse in 750ms? Probably some nerdy developer who's trying to double click using a finger and mouse :) |
I’m fairly certain that the “click” isn’t an emulated event, but the somewhat input device agnostic event (similar to “input” instead of key* events on form elements). The 300ms delay was to allow for double-tap to zoom gestures (and potentially other accessibility features). Is there any idea or testing how this would interact with other browser features that disable the 300ms delay such as:
|
You mean like this? http://smus.com/touch-laptop-experiments/ |
@s0meone Oh right, the click/tap event has "intentional" delays, but all other emulated mouse events I assume would be called immediately. |
That looks pretty awesome! Never seen it before. But I think when you're about to support this interaction you probably won't use a tap handler but handle all the events separately. @syranide |
@iamdustan I've tested this patch in the Chrome device emulator, which only has a delay of a few milliseconds. Ignoring the events works perfect. Log: touch event topTouchStart 1409062331329 react-with-addons.js?body=1:16870 |
@s0meone I was talking about the "browser-imposed" click delay. Hmm, it's interesting that there's a delay on Intuitively I would expect emulated mouse events to fire in the same frame as the touch events (but not necessarily with the same timestamp), if so, simply setting a global And I'm assuming you've checked that there aren't properties added/set for the emulated mouse event that would indicate that it's emulated? |
I did some testing today, here are my results. Please tell me if I'm testing wrong or am making the wrong assumptions. Looks like the events are triggered in separate frames. So resetting a global variable has no effect.
The example above resets the
I also tested with expensive touch handlers to explain why mouse events are delayed (besides the Example fiddle with expensive touch handlers: http://jsfiddle.net/es9qzL1f/ Log from iOS simulator:
Example fiddle without expensive touch handlers: http://jsfiddle.net/es9qzL1f/2/ Log from iOS simulator:
So it looks like the 300ms applies to the Then there is a seperate problem, the mouse events are not triggered on the same component as the touch events. When you look at this fiddle http://jsfiddle.net/2k4pzjth/ you can see (in the console) that the mouse events are triggered on the container, not on the clicked element (the box).
When we're not moving the box, all handlers are triggered on both components: http://jsfiddle.net/2k4pzjth/1/
This isn't a problem in React, but in webkit probably. Tested in Chrome and on iOS. Same example without React: http://jsfiddle.net/37qmsrkf/1/ When running this example in Firefox (with and without touch sensor emulation), all handlers are called on the correct targets. The event model, weird stuff :) So my conclusion after all these tests is that ignoring the mouse events is probably the best workaround for these problems. The 750ms threshold could be lowered but it totally depends on how expensive your touch handlers are and how fast the device is you're running on. |
@s0meone: I took the liberty of separating out your changes from the rest of React: |
@appsforartists: Thanks for your pull request. I'll take a look and merge it as soon as possible. |
I'm in the middle of debugging an issue in Chrome for Android where I'm getting a second Here are the logs on Chrome for MacOS, Safari for iOS, and Chrome for Android. Interestingly, I am unable to replicate this issue in ChromeOS with a touchscreen - it is very Android-specific. Unfortunately, I don't have the Chromebook handy to log with.
|
I just wrote a reduction for that issue and noticed it's the same one @s0meone is talking about above. |
@appsforartists did a great job in extracting my fix in a separate package. For anyone interested, it's on npm now: https://www.npmjs.org/package/react-tap-event-plugin |
@s0meone @appsforartists how are you unit testing this? Using |
What's unit testing? 😝 Less snarkfully, I'm using this in a prototype, so I haven't tried automated testing. |
I came across this post which includes a solution for eating all subsequent click events.
|
|
We're using
TapEventPlugin
in our application and it calls every handler twice. Those calls are triggered by different events - first time bytouchend
, and second time bymouseup
, which is generated by a browser as a click. This doesn't make any sense, so I thinkTapEventPlugin
should prevent triggering by amouseup
when event was triggered by touches.Not sure if there is a good way. I guess calling
e.preventDefault()
inside ofTapEventPlugin
is not the wisest solution, is it?The text was updated successfully, but these errors were encountered: