Handling both touch and click events in JS has always been a pain, thanks to the way these events interact. Importantly, since there are now many devices where users can both touch AND click, we can't simply switch all events over to touch events if we detect a touch. Here is the sequence these events fire:
touchstart
touchmove
touchend
-- 300ms delay --
mousedown
mousemove
mouseup
click
This presents a problem. If we bind our event handler only to click, we have a gross delay before anything happens. If we bind only to touchstart, mouse / pointer events won't fire. If we bind to both, then the handler will fire twice, once on touchstart, and then again 300ms later when click triggers.
To resolve this issue, I've put together a small mixin which sets a local variable "touched" on touchstart, and then only handles the click event if "touched" is false. In this way, the handler will only fire once, but we can bind to both touchstart and click safely.
Since React.js handles events synthetically, we need to initialize its own touch events with this line:
React.initializeTouchEvents(true);
Then we need to include the actual mixin:
var TouchMixin = {
touched: false,
handleTouch: function(fn) {
this.touched = true;
typeof fn === 'string' ? this[fn]() : this.event(fn);
},
handleClick: function(fn) {
if (this.touched) return this.touched = false;
typeof fn === 'string' ? this[fn]() : this.event(fn);
}
};
To use the mixin, add it to the mixins array on the component, and then either add a method named "event" to the component (this is your event handler), and bind 'handleClick' onClick and 'handleTouch' onTouchStart:
mixins: [TouchMixin],
event: function() {//DO STUFF HERE},
// attach these to elements in render method
onClick={ this.handleClick }
onTouchStart={ this.handleTouch }
Alternatively, you may wish to have a more descriptive method name, and / or multiple different event handlers. For this, bind the method name as a string to handleClick / handleTouch, and the mixin will sort it out for you:
mixins: [TouchMixin],
METHOD_NAME: function() {//DO STUFF HERE},
// attach these to elements in render method
onClick={ this.handleClick.bind(this, 'METHOD_NAME') }
onTouchStart={ this.handleTouch.bind(this, 'METHOD_NAME') }