├── LICENSE
├── README.md
├── drags.html
├── drags.js
├── index.html
├── taps.html
└── taps.js
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Skookum Digital Works
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 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Input Examples on the Web
2 |
3 | Examples of handling mouse, touch and pointer events on the web.
4 |
5 | These examples are intended for educational purposes; it is highly recommended
6 | against using any of these in a production environment. Use an alternative,
7 | such as:
8 |
9 | * [PointerGestures](https://github.com/Polymer/PointerGestures),
10 | * [HandJS](https://handjs.codeplex.com/)
11 | * [Hammer.js](http://hammerjs.github.io/)
12 |
13 | ## License
14 |
15 | The MIT License (MIT)
16 |
17 | Copyright (c) 2014 Skookum Digital Work
18 |
19 | Permission is hereby granted, free of charge, to any person obtaining a copy
20 | of this software and associated documentation files (the "Software"), to deal
21 | in the Software without restriction, including without limitation the rights
22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 | copies of the Software, and to permit persons to whom the Software is
24 | furnished to do so, subject to the following conditions:
25 |
26 | The above copyright notice and this permission notice shall be included in
27 | all copies or substantial portions of the Software.
28 |
29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
35 | THE SOFTWARE.
36 |
37 |
--------------------------------------------------------------------------------
/drags.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Tappable
6 |
7 |
8 |
31 |
32 |
33 |
34 | Index
35 | Taps
36 |
37 |
38 | Drag me about!
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/drags.js:
--------------------------------------------------------------------------------
1 | var hasPointer = !!(window.PointerEvent || window.navigator.msPointerEnabled);
2 | var hasTouch = !!window.TouchEvent || true;
3 |
4 | var POINTER_DOWN = 'MSPointerDown';
5 | var POINTER_UP = 'MSPointerUp';
6 | var POINTER_MOVE = 'MSPointerMove';
7 |
8 | if (window.PointerEvent) {
9 | POINTER_DOWN = 'pointerdown';
10 | POINTER_UP = 'pointerup';
11 | POINTER_MOVE = 'pointermove';
12 | }
13 | var log = document.querySelector('#log');
14 |
15 | var draggable = document.querySelector('#draggable');
16 |
17 | var events = {};
18 |
19 | /**
20 | * @param {Event} event
21 | * @param {Element} target
22 | * @return {String}
23 | */
24 | function id(event, target) {
25 | return event.type.slice(0, 5) + target.id;
26 | }
27 |
28 | if (hasPointer) {
29 | draggable.addEventListener(POINTER_DOWN, dragStart, false);
30 | }
31 | else {
32 | draggable.addEventListener('mousedown', dragStart, false);
33 |
34 | if (hasTouch) {
35 | draggable.addEventListener('touchstart', dragStart, false);
36 | }
37 | }
38 |
39 | function set(id, prop, value) {
40 | if (typeof prop === 'object' && typeof value === 'undefined') {
41 | events[id] = value;
42 | return;
43 | }
44 | if (typeof id !== 'string') id = idFor(id);
45 | if (typeof events[id] === 'undefined') {
46 | events[id] = {};
47 | }
48 | // ignore compatibility events
49 | if (typeof events[id][prop] === 'undefined') {
50 | events[id][prop] = value;
51 | }
52 | }
53 |
54 | function dragStart(event) {
55 | // ignore non-left clicks
56 | if (event.type === 'mousedown' && event.which !== 1) return;
57 |
58 | var target = event.currentTarget;
59 | var type = /^(pointer|mouse|touch)/i.exec(event.type)[0];
60 | var moveEvent = type + 'move';
61 | var endEvent = type + 'up';
62 | var o = extract(event);
63 | var pos = extractPositioning(target);
64 |
65 | // assume uniqueness in key
66 | switch (event.type.toLowerCase()) {
67 | case 'mspointerdown':
68 | case 'pointerdown':
69 | event.currentTarget.setPointerCapture(event.pointerId);
70 | /* Direct all pointer events to JavaScript code. */
71 | event.currentTarget.style.msTouchAction = 'none';
72 | event.currentTarget.style.touchAction = 'none';
73 | break;
74 | case 'touchstart':
75 | o.x = event.changedTouches[0].clientX;
76 | o.y = event.changedTouches[0].clientY;
77 | endEvent = type + 'end';
78 | break;
79 | case 'mousedown':
80 | target = document.body;
81 | break;
82 | }
83 | if (event.type.toLowerCase() !== 'mousedown') {
84 | // prevent the cascade
85 | event.preventDefault();
86 | }
87 |
88 | var __id = id(event, target);
89 | set(__id, 'x', o.x - pos.x);
90 | set(__id, 'y', o.y - pos.y);
91 | set(__id, 'originalTarget', o.originalTarget);
92 | set(__id, 'timeStamp', o.timeStamp);
93 |
94 | target.addEventListener(moveEvent, dragMove, false);
95 | target.addEventListener(endEvent, dragEnd, false);
96 | }
97 |
98 | function dragMove(event) {
99 | var target = event.currentTarget;
100 | var o = extract(event);
101 | event.preventDefault();
102 |
103 | if (/^mouse/.test(event.type))
104 | target = document.body;
105 |
106 | var __id = id(event, target);
107 | var oo = events[__id];
108 | if (typeof oo === 'undefined') return;
109 |
110 | var xDiff = o.x - oo.x;
111 | var yDiff = o.y - oo.y;
112 |
113 | requestAnimationFrame(function() {
114 | position(oo.originalTarget, xDiff, yDiff);
115 | });
116 | }
117 |
118 | function dragEnd(event) {
119 | var target = event.currentTarget;
120 | var o = extract(event);
121 | var type = /^(pointer|mouse|touch)/.exec(event.type)[0];
122 | var moveEvent = type + 'move';
123 | var endEvent = type + 'up';
124 |
125 | if (event.type === POINTER_DOWN)
126 | event.currentTarget.releasePointerCapture(event.pointerId);
127 | else if (event.type === 'mousedown')
128 | target = document.body;
129 |
130 | var __id = id(event, target);
131 | var oo = events[__id];
132 | target.removeEventListener(moveEvent, dragEnd);
133 | target.removeEventListener(event.type, dragEnd);
134 |
135 | var xDiff = o.x - oo.x;
136 | var yDiff = o.y - oo.y;
137 |
138 | requestAnimationFrame(function() {
139 | position(oo.originalTarget, xDiff, yDiff);
140 | delete events[__id];
141 | });
142 | }
143 |
144 | function extract(event) {
145 | var x = event.clientX, y = event.clientY;
146 | if (event.type.match(/touch/)) {
147 | x = event.changedTouches[0].clientX;
148 | y = event.changedTouches[0].clientY;
149 | }
150 |
151 | return {
152 | originalTarget: event.currentTarget,
153 | timeStamp: event.timeStamp || Date.now(),
154 | x: x,
155 | y: y,
156 | };
157 | }
158 |
159 | function position(node, x, y) {
160 | node.style.transform = 'translate(' + x + 'px ,' + y + 'px)';
161 | node.style.webkitTransform = 'translate(' + x + 'px ,' + y + 'px)';
162 | }
163 |
164 | function extractPositioning(node) {
165 | var transform = node.style.transform || node.style.webkitTransform;
166 | var x = 0, y = 0;
167 | if (typeof transform !== 'undefined') {
168 | var parts = transform.match(/(-?\d+)/g);
169 | x = parts ? Number(parts[0]) : 0;
170 | y = parts ? Number(parts[1]) : 0;
171 | }
172 | return {x: x, y: y};
173 | }
174 |
175 |
176 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Tappable
6 |
7 |
8 |
23 |
24 |
25 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/taps.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Tappable
6 |
7 |
8 |
42 |
43 |
44 |
45 | Index
46 | Drags
47 |
48 | Tap.
49 |
Use JS to manage `tap` intent and fire as soon as possible.
50 |
51 | Click.
52 |
Bind to touch and pointer start events to track time from intent start to browser action.
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/taps.js:
--------------------------------------------------------------------------------
1 | var __slice = function(a, b) { return Array.prototype.slice.call(a, b); };
2 | var hasPointer = !!(window.PointerEvent || window.navigator.msPointerEnabled);
3 | var hasTouch = !!window.TouchEvent;
4 |
5 | var ENTER_KEY = 13;
6 |
7 | var POINTER_DOWN = 'MSPointerDown';
8 | var POINTER_UP = 'MSPointerUp';
9 | var POINTER_MOVE = 'MSPointerMove';
10 |
11 | if (window.PointerEvent) {
12 | POINTER_DOWN = 'pointerdown';
13 | POINTER_UP = 'pointerup';
14 | POINTER_MOVE = 'pointermove';
15 | }
16 |
17 | var tappable = document.querySelector('#tappable');
18 | var clickable = document.querySelector('#clickable');
19 | var output = document.querySelector('#output');
20 |
21 | var events = {};
22 |
23 | if (hasPointer) {
24 | tappable.addEventListener(POINTER_DOWN, tapStart, false);
25 | clickable.addEventListener(POINTER_DOWN, clickStart, false);
26 | }
27 | else {
28 | tappable.addEventListener('mousedown', tapStart, false);
29 | clickable.addEventListener('mousedown', clickStart, false);
30 |
31 | if (hasTouch) {
32 | tappable.addEventListener('touchstart', tapStart, false);
33 | clickable.addEventListener('touchstart', clickStart, false);
34 | }
35 | }
36 |
37 | clickable.addEventListener('click', clickEnd, false);
38 |
39 | function id(eventType, target) {
40 | return eventType.slice(0, 5) + target.id;
41 | }
42 |
43 | function idFor(eventOrType, target) {
44 | if (typeof eventOrType === 'string') {
45 | return id(eventOrType, target);
46 | }
47 | return id(eventOrType.type, eventOrType.target);
48 | }
49 |
50 | function set(id, prop, value) {
51 | if (typeof id !== 'string') id = idFor(id);
52 | if (typeof events[id] === 'undefined') {
53 | events[id] = {};
54 | }
55 | // ignore compatibility events
56 | if (typeof events[id][prop] === 'undefined') {
57 | events[id][prop] = value;
58 | }
59 | }
60 |
61 | function get(id, prop) {
62 | if (typeof id !== 'string') id = idFor(id);
63 | return events[id][prop] || null;
64 | }
65 |
66 | function clickStart(event) {
67 | set(id('click', clickable), 'clickStart', Date.now());
68 | }
69 |
70 | function diff(start, end) {
71 | return Math.abs(start - end);
72 | }
73 |
74 | function tapStart(event) {
75 | bindEventsFor(event.type, event.target);
76 | if (typeof event.setPointerCapture === 'function') {
77 | event.currentTarget.setPointerCapture(event.pointerId);
78 | }
79 | // prevent the cascade
80 | event.preventDefault();
81 | set(event, 'tapStart', Date.now());
82 | }
83 |
84 | function tapEnd(target, event) {
85 | unbindEventsFor(event.type, target);
86 | var _id = idFor(event);
87 | log('Tap', diff(get(_id, 'tapStart'), Date.now()));
88 | setTimeout(function() {
89 | delete events[_id];
90 | });
91 | }
92 |
93 | function clickEnd(event) {
94 | var _id = id('click', clickable);
95 | log('Click', diff(get(_id, 'clickStart'), Date.now()));
96 | setTimeout(function() {
97 | delete events[_id];
98 | });
99 | }
100 |
101 | function bindEventsFor(type, target) {
102 | var end = curry(tapEnd, target);
103 | set(idFor(type, target), 'callback', end);
104 |
105 | if (type === 'mousedown') {
106 | document.addEventListener('mouseup', end, false);
107 | }
108 | else if (type === 'touchstart') {
109 | target.addEventListener('touchend', end, false);
110 | }
111 | else {
112 | target.addEventListener(POINTER_UP, end, false);
113 | }
114 | }
115 |
116 | function unbindEventsFor(type, target) {
117 | var id = idFor(type, target);
118 | var endEvent = events[id].callback;
119 | document.removeEventListener('mouseup', endEvent);
120 | target.removeEventListener('touchend', endEvent);
121 | target.removeEventListener(POINTER_UP, endEvent);
122 | }
123 |
124 | function curry(fn /*, ...args*/) {
125 | var args = __slice(arguments, 1);
126 | return function() {
127 | fn.apply(this, args.concat(__slice(arguments, 0)));
128 | };
129 | }
130 |
131 | function log(type, length) {
132 | var li = document.createElement('li');
133 | li.innerHTML = '' + type + ' ' + length + 'ms';
134 | output.insertBefore(li, output.firstChild);
135 | }
136 |
137 | function clear() {
138 | output.innerHTML = '';
139 | }
140 |
141 |
--------------------------------------------------------------------------------