-
Notifications
You must be signed in to change notification settings - Fork 50.9k
touchstart preventDefault() does not prevent click event. #9809
Description
Do you want to request a feature or report a bug?
Bug
What is the current behavior?
Calling e.preventDefault() on a synthetic onTouchStart event fails to prevent the click event. I also tried e.nativeEvent.preventDefault(), but this didn't make any difference.
If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via https://jsfiddle.net or similar (template: https://jsfiddle.net/84v837e9/).
Here's a div which is supposed to handle a hover case but not process a click the first time is is tapped via touch (click on desktop is fine). However tapping with touch (on a mobile device or using dev tools touch emulation) will trigger both touchstart and click immediately.
const style = {
background: 'red',
width: 100,
height: 100,
// to ensure `touchstart` `preventDefault()` is allowed on mobile
touchAction: 'none'
};
class SomeButton extends React.Component {
constructor (props) {
super(props);
this.state = {
hover: false,
click: false
};
}
render () {
return (
<div
style={style}
onMouseEnter={() => this.setState({ hover: true })}
onClick={() => this.setState({ click: true })}
onTouchStart={e => {
if (!this.state.hover) {
e.preventDefault(); // doesn't work!
this.setState({ hover: true });
}
}}
>
{this.state.hover && 'hover!'}
{this.state.click && 'click!'}
</div>
);
}
}However if I move the touchstart listener to componentDidMount and use the normal DOM API, everything works:
// ...
class SomeButton extends React.Component {
constructor (props) {
// ...
}
componentDidMount () {
this.elem.addEventListener('touchstart', e => {
if (!this.state.hover) {
e.preventDefault(); // WORKS!
this.setState({ hover: true });
}
});
}
render () {
return (
<div
ref={elem => this.elem = elem}
{ /* ... (removed onTouchStart) ... */}
>
{/* ... */}
</div>
);
}
}What is the expected behavior?
The first time a touchstart is processed, we only treat it as a hover, and wait to process the click event until after the next touchstart. If the pointer is a mouse, both events can be processed at once.
Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
React 15.5.4. Not sure about previous React versions. Chrome for Android, Chrome for Mac emulating touch, Firefox for Mac emulating touch.