var endEvents = [ 'touchend' ] module.exports = Tap // default tap timeout in ms Tap.timeout = 200 function Tap(callback, options) { options = options || {} // if the user holds his/her finger down for more than 200ms, // then it's not really considered a tap. // however, you can make this configurable. var timeout = options.timeout || Tap.timeout // to keep track of the original listener listener.handler = callback return listener // el.addEventListener('touchstart', listener) function listener(e1) { // tap should only happen with a single finger if (!e1.touches || e1.touches.length > 1) return var el = e1.target var context = this var args = arguments; var timeout_id = setTimeout(cleanup, timeout) el.addEventListener('touchmove', cleanup) endEvents.forEach(function (event) { el.addEventListener(event, done) }) function done(e2) { // since touchstart is added on the same tick // and because of bubbling, // it'll execute this on the same touchstart. // this filters out the same touchstart event. if (e1 === e2) return cleanup() // already handled if (e2.defaultPrevented) return // overwrite these functions so that they all to both start and events. var preventDefault = e1.preventDefault var stopPropagation = e1.stopPropagation e1.stopPropagation = function () { stopPropagation.call(e1) stopPropagation.call(e2) } e1.preventDefault = function () { //preventDefault.call(e1) preventDefault.call(e2) } // calls the handler with the `end` event, // but i don't think it matters. callback.apply(context, args) } // cleanup end events // to cancel the tap, just run this early function cleanup(e2) { // if it's the same event as the origin, // then don't actually cleanup. // hit issues with this - don't remember if (e1 === e2) return clearTimeout(timeout_id) el.removeEventListener('touchmove', cleanup) endEvents.forEach(function (event) { el.removeEventListener(event, done) }) } } }