├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── demo ├── demo.js └── index.html ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | demo/demo-bundle.js 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.2.0 4 | 5 | - Add `useCapture` argument to `createTapListener`. 6 | 7 | ## 0.1.0 8 | 9 | - Initial release. 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 David Clark 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # teeny-tap 2 | 3 | Listen for both clicks and click-like touches (not scrolls or drags). 4 | 5 | This library is very small and simple and focused, without any dependencies. 6 | It is not a touch gesture library or a complete fastclick-style solution. 7 | Plenty of similar libraries exist, but none of them seemed exactly right for 8 | my simple needs. For more about the purpose of the library, read ["Why?"](#why). 9 | 10 | This library is heavily inspired by [tap.js](https://github.com/alexgibson/tap.js), 11 | which is no longer maintained. 12 | 13 | **[Check out the demo](http://davidtheclark.github.io/teeny-tap/demo/).** 14 | 15 | (If the demo works on your phone, would you mind tweeting me your specs, 16 | `@davidtheclark`? That way I can list the devices where we know it works. 17 | And of course let me know if you find any bugs!) 18 | 19 | ## Installation 20 | 21 | ``` 22 | npm install teeny-tap 23 | ``` 24 | 25 | You will need to be compiling CommonJS modules (browserify or webpack). 26 | 27 | ### Browser Support 28 | 29 | IE9+ and everything help, I hope. Testing underway. 30 | (If you can help out by trying the demo on your mobile device, please do!) 31 | 32 | ## Usage 33 | 34 | ```js 35 | var createTapListener = require('teeny-tap'); 36 | 37 | var docTapListener = createTapListener(document.documentElement, function(e) { 38 | console.log('A tap happened!'); 39 | }); 40 | 41 | // ... 42 | docTapListener.remove(); 43 | ``` 44 | 45 | That's it. Very simple. You create and remove listeners. 46 | 47 | ### API 48 | 49 | #### `var tapListenerInstance = createTapListener(element, callback[, useCapture])` 50 | 51 | Adds a tap listener on `element`, using `addEventListener()`. 52 | When there's a tap, `callback` is invoked with 53 | the relevant `event` as its argument (either a `click` or `touchend` event). 54 | 55 | **Returns an object with a `remove` function, for removing the listener.** 56 | 57 | #### `tapListenerInstance.remove()` 58 | 59 | Remove the listener that you added when you created `tapListenerInstance`. 60 | 61 | ## Why? 62 | 63 | For 64 | [react-aria-menubutton](https://github.com/davidtheclark/react-aria-menubutton), 65 | I need to close an open menu when the user taps/clicks outside of it. 66 | 67 | The click event wasn't reliable: mobile Safari screws it up. 68 | So I needed to **distinguish, on touch devices, between *touches 69 | that are scrolling or dragging* and *touches that are clicking*, 70 | in situations where the regular `click` event doesn't work**. 71 | Existing solutions that I found weren't satisfactory for a few reasons: 72 | 73 | - jQuery or other dependencies I didn't want or need. 74 | 75 | - Many just threw in a `touchstart` or `touchend` 76 | listener, in addition to the click; but that alone won't distinguish 77 | between tapping and scrolling/dragging, so it's insufficient. 78 | The menu might close when you, say, scroll down in order to see more of it. 79 | Egad! 80 | 81 | - One solution to the detect-clicks-outside-an-element problem is to add an 82 | underlay that covers the page, beneath the element, and listen for clicks on that. 83 | However, this prevents the click from getting through and actually 84 | *doing* something; so if I click a link outside the open menu, 85 | that click only closes the menu, and I have to click *again* to 86 | go where I wanted to go. Non-optimal. 87 | 88 | - A number of solutions just use `click` events, which won't 89 | work on most browsers on most iPhones and iPads, due to 90 | mobile Safari's unique and unpleasant handling of click events. 91 | 92 | - Some went the other way, and had more fine-tuning than I want or need. 93 | 94 | [tap.js](https://github.com/alexgibson/tap.js) pretty much provided exactly what I needed, 95 | but that library is no longer maintained so I made this one to carry the torch, 96 | to fill the need. 97 | 98 | But know that *I do not like that this exists, do not want it to need to exist*. 99 | If you know of a way to accomplish the same goal without this library, 100 | please let me know! 101 | -------------------------------------------------------------------------------- /demo/demo.js: -------------------------------------------------------------------------------- 1 | var tapListener = require('..'); 2 | var count = 0; 3 | var docTapListener; 4 | 5 | document.getElementById('init').addEventListener('click', function(e) { 6 | e.stopPropagation(); 7 | if (docTapListener) return; 8 | docTapListener = true; 9 | docTapListener = tapListener(document.documentElement, registerTap); 10 | }); 11 | 12 | document.getElementById('remove').addEventListener('click', function(e) { 13 | e.stopPropagation(); 14 | if (docTapListener) docTapListener.remove(); 15 | docTapListener = null; 16 | }); 17 | 18 | function registerTap(e) { 19 | console.log(e.type); 20 | count++; 21 | document.getElementById('count').innerHTML = count; 22 | } 23 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 |35 | This library's purpose is to distinguish on touch devices between 36 | taps and non-taps (e.g. scrolls and drags) in situations where 37 | the "click" event will not work. 38 |
39 | 40 |41 | If you find anything wrong in this demo, please file a bugs 42 | back in the repo. 43 |
44 | 45 |46 | Click this button to initiate the tap listener: 47 |
48 | 49 |50 | 53 |
54 | 55 |56 | Every time you tap (or click), the number at the bottom of the screen should increment. 57 | When you touch-scroll or -drag, it should not. 58 |
59 | 60 |61 | Also, try clicking this button to remove the tap listener: 62 |
63 | 64 |65 | 68 |
69 | 70 |71 | And here is a lot of space to try scrolling in. 72 | (Scrolls should not cause tap events.) 73 |
74 | 75 |