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

onTouchTap fires twice on touch devices ? #77

Open
jasan-s opened this issue Jun 26, 2016 · 11 comments
Open

onTouchTap fires twice on touch devices ? #77

jasan-s opened this issue Jun 26, 2016 · 11 comments

Comments

@jasan-s
Copy link

jasan-s commented Jun 26, 2016

Its very likely i'm on implementing something correctly, so here is the problem: I have a button that i'm using onTouchTap to open and close a modal. On non-touch devices the modal functional as expected, however on touch devices the button onTouchTap fires twice.

@ericalli
Copy link

+1

@bkbrooks
Copy link

bkbrooks commented Jul 13, 2016

This is happening for me on modal windows as well. When I click on a button to open a modal, it will fire a second onTouchTap event which ends up tapping on something inside the modal. This is very frustrating when there is a link or another button inside the modal.

It also happens on close of the modal. When I click on a close button on the modal, it will trigger any link or button underneath the spot that I clicked to close.

We are using material-ui dialogs for our modal windows.
http://www.material-ui.com/#/components/dialog

@nadangergeo
Copy link

Any news on this issue?

@deepfriedbrain
Copy link

I'm facing the same issue when using Material UI.

@legendweny
Copy link

+1

@JamesAlias
Copy link

I found a solution so it may help you too.

After the onTouchTap event another onClick event is fired after a delay (~300ms), whether the onTouchTap is handled or not.
The onClick event is triggered on an element at the same position as the onTouchTap event but after the delay.

FIX:
Adding e.preventDefault to the onTouchTap event handler prevented the onClick event for me.

Example:
<Button onTouchTap={(e) => {e.preventDefault(); handleTouch();}}

Hope this helps.

@jamespizzurro
Copy link

jamespizzurro commented Nov 18, 2016

This is probably the wrong place for this, but if you're using onTouchTap everywhere instead of onClick, you can safely not use react-tap-event-plugin and replace it with a modified version of React's internal event plugin TapEventPlugin that has been modified to invoke preventDefault() on the native click event, similar to what @JamesAlias mentions above. This method, however, avoids having to invoke preventDefault() everywhere onTouchTap is used and instead just define it once.

I cloned react-dom/lib/TapEventPlugin.js from my project's node_modules directory (this is the current location of that file as of React 15.4.0, when it moved) and appended the following line after if (isEndish(topLevelType) && distance < tapMoveThreshold) {, i.e. inside this conditional block:

nativeEvent.preventDefault(); // prevent ghost clicks

After this, I had to change the paths of the dependencies defined towards the top of the cloned file, because they were using relative paths that are no longer valid in the cloned version:

var EventPluginUtils = require('react-dom/lib/EventPluginUtils');
var EventPropagators = require('react-dom/lib/EventPropagators');
var SyntheticUIEvent = require('react-dom/lib/SyntheticUIEvent');
var TouchEventUtils = require('fbjs/lib/TouchEventUtils');
var ViewportMetrics = require('react-dom/lib/ViewportMetrics');

Finally, to inject this plugin, I do the following immediately before ReactDOM.render is invoked:

import EventPluginHub from 'react-dom/lib/EventPluginHub';
import TapEventPlugin from './TapEventPlugin';
EventPluginHub.injection.injectEventPluginsByName({
  'TapEventPlugin': TapEventPlugin
});

(where './TapEventPlugin' is the path to your cloned version of TapEventPlugin)

So far, I've had no problems with this setup. I played around in a variety of browsers and platforms before posting this--both on desktop and mobile--but milage may vary. Just needed to share this somewhere before I forgot!

@ethan-deng
Copy link

I also saw this is happening with AppBar's onLeftIconButtonTouchTap on Android Chrome. The touch tap is triggered twice and the solution that use e.preventDefault() in event handler helps.

@ethan-deng
Copy link

Okay this is called ghost click and documented here with solution.

https://github.com/zilverline/react-tap-event-plugin

Ignoring ghost clicks

When a tap happens, the browser sends a touchstart and touchend, and then 300ms later, a click event. This plugin ignores the click event if it has been immediately preceeded by a touch event (within 750ms of the last touch event).

Occasionally, there may be times when the 750ms threshold is exceeded due to slow rendering or garbage collection, and this causes the dreaded ghost click.

The 750ms threshold is pretty good, but sometimes you might want to override that behaviour. You can do this by supplying your own shouldRejectClick function when you inject the plugin.

The following example will simply reject all click events, which you might want to do if you are always using onTouchTap and only building for touch devices:

var React = require('react'),
injectTapEventPlugin = require("react-tap-event-plugin");
injectTapEventPlugin({
shouldRejectClick: function (lastTouchEventTimestamp, clickEventTimestamp) {
return true;
}
});

@lqzerogg
Copy link

@ethan-deng It seems that shouldRejectClick does not work even if I always return true in the method.

@KerenChandran
Copy link

So I'm still experiencing this issue and tried the shouldRejectClick snippet, but that doesn't work on react 15.6.0. The e.preventDefault snippet worked, so wondering if we should update docs or further investigate this issue.

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