├── .gitignore ├── component.json ├── package.json ├── LICENSE ├── README.md └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store* 3 | *.log 4 | *.gz 5 | 6 | node_modules 7 | coverage 8 | -------------------------------------------------------------------------------- /component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tap-event", 3 | "description": "Create tap event listeners", 4 | "repo": "component/tap-event", 5 | "version": "1.0.0", 6 | "main": "index.js", 7 | "scripts": [ 8 | "index.js" 9 | ], 10 | "license": "MIT" 11 | } 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tap-event", 3 | "description": "Create tap event listeners", 4 | "version": "1.0.0", 5 | "author": "Jonathan Ong (http://jongleberry.com)", 6 | "license": "MIT", 7 | "repository": "component/tap-event", 8 | "keywords": [ 9 | "tap", 10 | "event", 11 | "listener" 12 | ], 13 | "files": [ 14 | "index.js" 15 | ], 16 | "devDependencies": { 17 | "standardberry": "^1.0.0" 18 | }, 19 | "scripts": { 20 | "lint": "standardberry index.js" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Jonathan Ong me@jongleberry.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tap Event 2 | 3 | Make your `touchstart` event listeners into a `tap` event listener! 4 | 5 | What is "correct" behavior? The `tap` event: 6 | 7 | - shouldn't be triggered until the user removes his/her finger from the surface of the screen. 8 | - shouldn't be triggered when the user moves his/her finger at all (ie it should not interfere with drag events). 9 | - shouldn't be triggered if there's ever more than a single finger on the surface at all. 10 | - should never trigger the `click` event. 11 | 12 | ## API 13 | 14 | ```js 15 | var tap = require('tap-event') 16 | 17 | var el = document.querySelector('#container') 18 | 19 | // the event you want to handle 20 | function changeLocation(e) { 21 | // e.preventDefault() is already called! 22 | location.href = this.href 23 | } 24 | 25 | // wrap the listener 26 | var listener = tap(changeLocation) 27 | 28 | // listen to `touchstart` 29 | el.addEventListener('touchstart', listener) 30 | 31 | // remove the listener 32 | el.removeEventListener('touchstart', listener) 33 | ``` 34 | 35 | or, more succinctly: 36 | 37 | ```js 38 | document.querySelector('#container').addEventListener('touchstart', tap(function (e) { 39 | location.href = this.href 40 | })) 41 | ``` 42 | 43 | To set a custom timeout (default is `200ms`), you have two options: 44 | 45 | ```js 46 | // set globally 47 | tap.timeout = 300 48 | 49 | // set per instance 50 | tap(function (e) { 51 | 52 | }, { 53 | timeout: 300 54 | }) 55 | ``` 56 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var cancelEvents = [ 2 | 'touchmove', 3 | 'touchcancel', 4 | 'touchstart', 5 | ] 6 | 7 | var endEvents = [ 8 | 'touchend', 9 | ] 10 | 11 | module.exports = Tap 12 | 13 | // default tap timeout in ms 14 | Tap.timeout = 200 15 | 16 | function Tap(callback, options) { 17 | options = options || {} 18 | // if the user holds his/her finger down for more than 200ms, 19 | // then it's not really considered a tap. 20 | // however, you can make this configurable. 21 | var timeout = options.timeout || Tap.timeout 22 | 23 | // to keep track of the original listener 24 | listener.handler = callback 25 | 26 | return listener 27 | 28 | // el.addEventListener('touchstart', listener) 29 | function listener(e1) { 30 | // tap should only happen with a single finger 31 | if (!e1.touches || e1.touches.length > 1) return 32 | 33 | var el = this 34 | var args = arguments 35 | 36 | var timeout_id = setTimeout(cleanup, timeout) 37 | 38 | cancelEvents.forEach(function (event) { 39 | document.addEventListener(event, cleanup) 40 | }) 41 | 42 | endEvents.forEach(function (event) { 43 | document.addEventListener(event, done) 44 | }) 45 | 46 | function done(e2) { 47 | // since touchstart is added on the same tick 48 | // and because of bubbling, 49 | // it'll execute this on the same touchstart. 50 | // this filters out the same touchstart event. 51 | if (e1 === e2) return 52 | 53 | cleanup() 54 | 55 | // already handled 56 | if (e2.defaultPrevented) return 57 | 58 | // overwrite these functions so that they all to both start and events. 59 | var preventDefault = e1.preventDefault 60 | var stopPropagation = e1.stopPropagation 61 | 62 | e2.stopPropagation = function () { 63 | stopPropagation.call(e1) 64 | stopPropagation.call(e2) 65 | } 66 | 67 | e2.preventDefault = function () { 68 | preventDefault.call(e1) 69 | preventDefault.call(e2) 70 | } 71 | 72 | // calls the handler with the `end` event, 73 | // but i don't think it matters. 74 | args[0] = e2 75 | callback.apply(el, args) 76 | } 77 | 78 | // cleanup end events 79 | // to cancel the tap, just run this early 80 | function cleanup(e2) { 81 | // if it's the same event as the origin, 82 | // then don't actually cleanup. 83 | // hit issues with this - don't remember 84 | if (e1 === e2) return 85 | 86 | clearTimeout(timeout_id) 87 | 88 | cancelEvents.forEach(function (event) { 89 | document.removeEventListener(event, cleanup) 90 | }) 91 | 92 | endEvents.forEach(function (event) { 93 | document.removeEventListener(event, done) 94 | }) 95 | } 96 | } 97 | } 98 | --------------------------------------------------------------------------------