Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React onClick and onTouchStart fired simultaneously

I have a div in my React app and I need to handle both clicks and touches. However, when I tap on a mobile, it fires both events.

If I swipe on a mobile or if I click on a normal browser, it works fine, only one event is fired in each case.

How can I handle this tap issue to not fire both events?

<div
  className={myClasses}
  onClick={this.myHandle}
  onTouchStart={this.myHandle}
  >&nbsp;
</div>
like image 206
Metarat Avatar asked Aug 10 '17 11:08

Metarat


4 Answers

There is a defined order of when the events get fired (source):

touchstart
Zero or more touchmove events, depending on movement of the finger(s)
touchend
mousemove
mousedown
mouseup
click

If you want to prevent a click event if a touch event is fired before, you can you can use event.preventDefault() in the touchend event handler to prevent the click event from firing.

function App() {
  const handleClick = () => {
    alert('click');
  };

  const handleTouchEnd = (event) => {
    alert('touchend');

    event.preventDefault();
  };

  return (
    <button
      type="button"
      onClick={handleClick}
      onTouchEnd={handleTouchEnd}
    >
      Click Me
    </button>
  );
}

However, this is not universally applicable. For example, you cannot prevent a click event by using a event.preventDefault() in a mousedown event. In case you are looking for a solution to this (not sure when this use case applies though), you would have to use a ref instead:

function App() {
  const prevent = React.useRef(false);

  const handleClick = () => {
    if (!prevent.current) {
      alert('click');
    } else {
      prevent.current = false;
    }
  };

  const handleMouseDown = () => {
    prevent.current = true;

    alert('mousedown');
  };

  return (
    <button
      type="button"
      onClick={handleClick}
      onMouseDown={handleMouseDown}
    >
      Click Me
    </button>
  );
}
like image 135
Robin Wieruch Avatar answered Oct 16 '22 10:10

Robin Wieruch


Solved this problem using similar events between touch and mouse. touchStart/mouseDown or touchEnd/mouseUp. It fires one or another, according to each situation.

<div
  className={myClasses}
  onMouseUp={this.myHandle}
  onTouchEnd={this.myHandle}
  >&nbsp;
</div>
like image 22
Metarat Avatar answered Oct 16 '22 10:10

Metarat


To avoid onClick() on touch devices you should check if the page is opened in touch devices or not.

To check weather it is opened in touch devices or not:

if (typeof document !== 'undefined') {
  var isTouch = 'ontouchstart' in document.documentElement;
}

Then modify {isTouch ? undefined : this.myHandle} to your mouse event:

<div
  className={myClasses}
  onClick={isTouch ? undefined : this.myHandle}
  onTouchStart={this.myHandle}
  >&nbsp;
</div>
like image 2
sonesh-aps Avatar answered Oct 16 '22 10:10

sonesh-aps


This is a few years late but found a solution that was really easy to implement. Looks like this:

import ReactDom from 'react-dom';

export default class YourClass extends Component {

componentDidMount(){
        ReactDOM.findDOMNode(this).addEventListener('touchstart', (e)=>{ 
            e.preventDefault(); 
            console.log("touchstart triggered");
        });
    }
}

Seems like it intercepts and stops all onClick calls on mobile which is exactly what I was looking for

like image 1
Vincent Lauffer Avatar answered Oct 16 '22 09:10

Vincent Lauffer