1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
|
---
title: Touch events
slug: Web/API/Touch_events
translation_of: Web/API/Touch_events
---
<div>{{DefaultAPISidebar("Touch Events(터치이벤트)")}}</div>
<div>일부분만 번역함.</div>
<div> </div>
<p> 터치를 기반으로 한 양질의 서비스를 제공하기 위해, Touch Events(터치이벤트)는 터치로 인한 움직임을 감지할 능력을 제공합니다.</p>
<p>터치 이벤트 인터페이스는 상대적으로 low-lever API이며 multi-touch interaction등의 다양한 동작을 특정해 트리거 할 수 있습니다. multi-touch interaction은 한 손가락이 터치패드에 처음 닫는 순간 시작합니다. 이후 다른 손가락들이 터치패드에 닿고 선택적으로 터치패드를 가로지를 수도 있습니다. interaction은 손가락들이 터치패드에서 떨어지면 끝납니다. interaction동안에 어플리케이션은 touch start, move, end 이벤트들을 받습니다.</p>
<p>Touch events는 동시에 여러 손가락으로 동시에 혹은 여러 지점에 터치 할 수 있다는 것만 제외하면 마우스 이벤트와 유사합니다. 터치이벤트 인터페이스는 현재 액션과 터치 지점을 캡슐화 합니다. single touch로 대표되는 interface는 터치된 정보등을 포함합니다.</p>
<h2 id="Definitions">Definitions</h2>
<dl>
<dt>Surface</dt>
<dd>터치 스크린, 터치 패드등을 포함합니다.</dd>
</dl>
<dl>
<dt>Touch point</dt>
<dd>손가락이나 터치 펜을 이용해 터치한 지점을 말합니다.</dd>
</dl>
<h2 id="Interfaces">Interfaces</h2>
<dl>
<dt>{{domxref("TouchEvent")}}</dt>
<dd>터치나 surface 위에서의 움직임들 입니다.</dd>
<dt>{{domxref("Touch")}}</dt>
<dd>touch surface위의 한 지점에 접촉할 때 발생합니다.</dd>
<dt>{{domxref("TouchList")}}</dt>
<dd>다중의 터치가 동시에 일어 났을 때 발생합니다.</dd>
</dl>
<h2 id="Example">Example</h2>
<p>This example tracks multiple touch points at a time, allowing the user to draw in a {{HTMLElement("canvas")}} with more than one finger at a time. It will only work on a browser that supports touch events.</p>
<div class="note"><strong>Note:</strong> The text below uses the term "finger" when describing the contact with the surface, but it could, of course, also be a stylus or other contact method.</div>
<h3 id="Create_a_canvas">Create a canvas</h3>
<pre class="brush: html"><canvas id="canvas" width="600" height="600" style="border:solid black 1px;">
Your browser does not support canvas element.
</canvas>
<br>
<button onclick="startup()">Initialize</button>
<br>
Log: <pre id="log" style="border: 1px solid #ccc;"></pre>
</pre>
<h3 id="Setting_up_the_event_handlers">Setting up the event handlers</h3>
<p>When the page loads, the <code>startup()</code> function shown below should be called by our {{HTMLElement("body")}} element's <code>onload</code> attribute (but in the example we use a button to trigger it, due to limitations of the MDN live example system).</p>
<pre class="brush: js">function startup() {
var el = document.getElementsByTagName("canvas")[0];
el.addEventListener("touchstart", handleStart, false);
el.addEventListener("touchend", handleEnd, false);
el.addEventListener("touchcancel", handleCancel, false);
el.addEventListener("touchmove", handleMove, false);
log("initialized.");
}
</pre>
<p>This simply sets up all the event listeners for our {{HTMLElement("canvas")}} element so we can handle the touch events as they occur.</p>
<h4 id="Tracking_new_touches">Tracking new touches</h4>
<p>We'll keep track of the touches in-progress.</p>
<pre class="brush: js">var ongoingTouches = [];
</pre>
<p>When a {{event("touchstart")}} event occurs, indicating that a new touch on the surface has occurred, the <code>handleStart()</code> function below is called.</p>
<pre class="brush: js">function handleStart(evt) {
evt.preventDefault();
log("touchstart.");
var el = document.getElementsByTagName("canvas")[0];
var ctx = el.getContext("2d");
var touches = evt.changedTouches;
for (var i = 0; i < touches.length; i++) {
log("touchstart:" + i + "...");
ongoingTouches.push(copyTouch(touches[i]));
var color = colorForTouch(touches[i]);
ctx.beginPath();
ctx.arc(touches[i].pageX, touches[i].pageY, 4, 0, 2 * Math.PI, false); // a circle at the start
ctx.fillStyle = color;
ctx.fill();
log("touchstart:" + i + ".");
}
}
</pre>
<p>This calls {{domxref("event.preventDefault()")}} to keep the browser from continuing to process the touch event (this also prevents a mouse event from also being delivered). Then we get the context and pull the list of changed touch points out of the event's {{domxref("TouchEvent.changedTouches")}} property.</p>
<p>After that, we iterate over all the {{domxref("Touch")}} objects in the list, pushing them onto an array of active touch points and drawing the start point for the draw as a small circle; we're using a 4-pixel wide line, so a 4 pixel radius circle will show up neatly.</p>
<h4 id="Drawing_as_the_touches_move">Drawing as the touches move</h4>
<p>Each time one or more fingers moves, a {{event("touchmove")}} event is delivered, resulting in our <code>handleMove()</code> function being called. Its responsibility in this example is to update the cached touch information and to draw a line from the previous position to the current position of each touch.</p>
<pre class="brush: js">function handleMove(evt) {
evt.preventDefault();
var el = document.getElementsByTagName("canvas")[0];
var ctx = el.getContext("2d");
var touches = evt.changedTouches;
for (var i = 0; i < touches.length; i++) {
var color = colorForTouch(touches[i]);
var idx = ongoingTouchIndexById(touches[i].identifier);
if (idx >= 0) {
log("continuing touch "+idx);
ctx.beginPath();
log("ctx.moveTo(" + ongoingTouches[idx].pageX + ", " + ongoingTouches[idx].pageY + ");");
ctx.moveTo(ongoingTouches[idx].pageX, ongoingTouches[idx].pageY);
log("ctx.lineTo(" + touches[i].pageX + ", " + touches[i].pageY + ");");
ctx.lineTo(touches[i].pageX, touches[i].pageY);
ctx.lineWidth = 4;
ctx.strokeStyle = color;
ctx.stroke();
ongoingTouches.splice(idx, 1, copyTouch(touches[i])); // swap in the new touch record
log(".");
} else {
log("can't figure out which touch to continue");
}
}
}
</pre>
<p>This iterates over the changed touches as well, but it looks in our cached touch information array for the previous information about each touch in order to determine the starting point for each touch's new line segment to be drawn. This is done by looking at each touch's {{domxref("Touch.identifier")}} property. This property is a unique integer for each touch, and remains consistent for each event during the duration of each finger's contact with the surface.</p>
<p>This lets us get the coordinates of the previous position of each touch and use the appropriate context methods to draw a line segment joining the two positions together.</p>
<p>After drawing the line, we call <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice"><code>Array.splice()</code></a> to replace the previous information about the touch point with the current information in the <code>ongoingTouches</code> array.</p>
<h4 id="Handling_the_end_of_a_touch">Handling the end of a touch</h4>
<p>When the user lifts a finger off the surface, a {{event("touchend")}} event is sent. We handle both of these the same way: by calling the <code>handleEnd()</code> function below. Its job is to draw the last line segment for each touch that ended and remove the touch point from the ongoing touch list.</p>
<pre class="brush: js">function handleEnd(evt) {
evt.preventDefault();
log("touchend");
var el = document.getElementsByTagName("canvas")[0];
var ctx = el.getContext("2d");
var touches = evt.changedTouches;
for (var i = 0; i < touches.length; i++) {
var color = colorForTouch(touches[i]);
var idx = ongoingTouchIndexById(touches[i].identifier);
if (idx >= 0) {
ctx.lineWidth = 4;
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(ongoingTouches[idx].pageX, ongoingTouches[idx].pageY);
ctx.lineTo(touches[i].pageX, touches[i].pageY);
ctx.fillRect(touches[i].pageX - 4, touches[i].pageY - 4, 8, 8); // and a square at the end
ongoingTouches.splice(idx, 1); // remove it; we're done
} else {
log("can't figure out which touch to end");
}
}
}
</pre>
<p>This is very similar to the previous function; the only real differences are that we draw a small square to mark the end and that when we call <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice"><code>Array.splice()</code></a>, we simply remove the old entry from the ongoing touch list, without adding in the updated information. The result is that we stop tracking that touch point.</p>
<h4 id="Handling_canceled_touches">Handling canceled touches</h4>
<p>If the user's finger wanders into browser UI, or the touch otherwise needs to be canceled, the {{event("touchcancel")}} event is sent, and we call the <code>handleCancel()</code> function below.</p>
<pre class="brush: js">function handleCancel(evt) {
evt.preventDefault();
log("touchcancel.");
var touches = evt.changedTouches;
for (var i = 0; i < touches.length; i++) {
var idx = ongoingTouchIndexById(touches[i].identifier);
ongoingTouches.splice(idx, 1); // remove it; we're done
}
}
</pre>
<p>Since the idea is to immediately abort the touch, we simply remove it from the ongoing touch list without drawing a final line segment.</p>
<h3 id="Convenience_functions">Convenience functions</h3>
<p>This example uses two convenience functions that should be looked at briefly to help make the rest of the code more clear.</p>
<h4 id="Selecting_a_color_for_each_touch">Selecting a color for each touch</h4>
<p>In order to make each touch's drawing look different, the <code>colorForTouch()</code> function is used to pick a color based on the touch's unique identifier. This identifier is an opaque number, but we can at least rely on it differing between the currently-active touches.</p>
<pre class="brush: js">function colorForTouch(touch) {
var r = touch.identifier % 16;
var g = Math.floor(touch.identifier / 3) % 16;
var b = Math.floor(touch.identifier / 7) % 16;
r = r.toString(16); // make it a hex digit
g = g.toString(16); // make it a hex digit
b = b.toString(16); // make it a hex digit
var color = "#" + r + g + b;
log("color for touch with identifier " + touch.identifier + " = " + color);
return color;
}
</pre>
<p>The result from this function is a string that can be used when calling {{HTMLElement("canvas")}} functions to set drawing colors. For example, for a {{domxref("Touch.identifier")}} value of 10, the resulting string is "#aaa".</p>
<h4 id="Copying_a_touch_object">Copying a touch object</h4>
<p>Some browsers (mobile Safari, for one) re-use touch objects between events, so it's best to copy the bits you care about, rather than referencing the entire object.</p>
<pre class="brush: js">function copyTouch(touch) {
return { identifier: touch.identifier, pageX: touch.pageX, pageY: touch.pageY };
}</pre>
<h4 id="Finding_an_ongoing_touch">Finding an ongoing touch</h4>
<p>The <code>ongoingTouchIndexById()</code> function below scans through the <code>ongoingTouches</code> array to find the touch matching the given identifier, then returns that touch's index into the array.</p>
<pre class="brush: js">function ongoingTouchIndexById(idToFind) {
for (var i = 0; i < ongoingTouches.length; i++) {
var id = ongoingTouches[i].identifier;
if (id == idToFind) {
return i;
}
}
return -1; // not found
}
</pre>
<h4 id="Showing_what's_going_on">Showing what's going on</h4>
<pre class="brush: js">function log(msg) {
var p = document.getElementById('log');
p.innerHTML = msg + "\n" + p.innerHTML;
}</pre>
<p>If your browser supports it, you can {{LiveSampleLink('Example', 'see it live')}}.</p>
<p><a href="http://jsfiddle.net/Darbicus/z3Xdx/10/">jsFiddle example</a></p>
<h2 id="Additional_tips">Additional tips</h2>
<p>This section provides additional tips on how to handle touch events in your web application.</p>
<h3 id="Handling_clicks">Handling clicks</h3>
<p>Since calling <code>preventDefault()</code> on a {{event("touchstart")}} or the first {{event("touchmove")}} event of a series prevents the corresponding mouse events from firing, it's common to call <code>preventDefault()</code> on {{event("touchmove")}} rather than {{event("touchstart")}}. That way, mouse events can still fire and things like links will continue to work. Alternatively, some frameworks have taken to refiring touch events as mouse events for this same purpose. (This example is oversimplified and may result in strange behavior. It is only intended as a guide.)</p>
<pre class="brush: js">function onTouch(evt) {
evt.preventDefault();
if (evt.touches.length > 1 || (evt.type == "touchend" && evt.touches.length > 0))
return;
var newEvt = document.createEvent("MouseEvents");
var type = null;
var touch = null;
switch (evt.type) {
case "touchstart":
type = "mousedown";
touch = evt.changedTouches[0];
break;
case "touchmove":
type = "mousemove";
touch = evt.changedTouches[0];
break;
case "touchend":
type = "mouseup";
touch = evt.changedTouches[0];
break;
}
newEvt.initMouseEvent(type, true, true, evt.originalTarget.ownerDocument.defaultView, 0,
touch.screenX, touch.screenY, touch.clientX, touch.clientY,
evt.ctrlKey, evt.altKey, evt.shiftKey, evt.metaKey, 0, null);
evt.originalTarget.dispatchEvent(newEvt);
}
</pre>
<h3 id="Calling_preventDefault()_only_on_a_second_touch">Calling preventDefault() only on a second touch</h3>
<p>One technique for preventing things like <code>pinchZoom</code> on a page is to call <code>preventDefault()</code> on the second touch in a series. This behavior is not well defined in the touch events spec, and results in different behavior for different browsers (i.e., iOS will prevent zooming but still allow panning with both fingers; Android will allow zooming but not panning; Opera and Firefox currently prevent all panning and zooming.) Currently, it's not recommended to depend on any particular behavior in this case, but rather to depend on meta viewport to prevent zooming.</p>
<h2 id="Specifications">Specifications</h2>
<table class="standard-table">
<tbody>
<tr>
<th scope="col">Specification</th>
<th scope="col">Status</th>
<th scope="col">Comment</th>
</tr>
<tr>
<td>{{SpecName('Touch Events 2', '#touch-interface', 'Touch')}}</td>
<td>{{Spec2('Touch Events 2')}}</td>
<td>Added <code>radiusX</code>, <code>radiusY</code>, <code>rotationAngle</code>, <code>force</code> properties</td>
</tr>
<tr>
<td>{{SpecName('Touch Events', '#touch-interface', 'Touch')}}</td>
<td>{{Spec2('Touch Events')}}</td>
<td>Initial definition.</td>
</tr>
</tbody>
</table>
<h2 id="Browser_compatibility">Browser compatibility</h2>
<p>{{CompatibilityTable}}</p>
<p>Note that unfortunately touch events may not fire at all on laptops with touch functionality (<a href="/en-US/docs/">test page</a>).</p>
<div id="compat-desktop">
<table class="compat-table">
<tbody>
<tr>
<th>Feature</th>
<th>Chrome</th>
<th>Edge</th>
<th>Firefox (Gecko)</th>
<th>Internet Explorer</th>
<th>Opera</th>
<th>Safari</th>
</tr>
<tr>
<td>Basic support</td>
<td>{{CompatChrome("22.0")}}</td>
<td>{{CompatVersionUnknown}}</td>
<td>{{CompatGeckoDesktop("18.0")}}<sup>[1]</sup><br>
{{CompatGeckoDesktop("52.0")}}<sup>[2]</sup></td>
<td>{{CompatNo}}</td>
<td>{{CompatNo}}</td>
<td>{{CompatNo}}</td>
</tr>
</tbody>
</table>
</div>
<div id="compat-mobile">
<table class="compat-table">
<tbody>
<tr>
<th>Feature</th>
<th>Android</th>
<th>Android Webview</th>
<th>Chrome for Android</th>
<th>Edge</th>
<th>Firefox Mobile (Gecko)</th>
<th>Firefox OS</th>
<th>IE Mobile</th>
<th>Opera Mobile</th>
<th>Safari Mobile</th>
</tr>
<tr>
<td>Basic support</td>
<td>{{CompatVersionUnknown}}</td>
<td>{{CompatVersionUnknown}}</td>
<td>{{CompatVersionUnknown}}</td>
<td>{{CompatVersionUnknown}}</td>
<td>{{CompatGeckoMobile("6.0")}}</td>
<td>{{CompatVersionUnknown}}</td>
<td>11</td>
<td>{{CompatVersionUnknown}}</td>
<td>{{CompatVersionUnknown}}</td>
</tr>
</tbody>
</table>
</div>
<p>[1] The <code>dom.w3c_touch_events.enabled</code> tri-state preference can be used to disable (0), enable (1), and auto-detect (2) support for standard touch events; by default, they're on auto-detect (2).</p>
<p>As of Gecko 24.0 {{geckoRelease("24.0")}}, the touch events support introduced with Gecko 18.0 {{geckoRelease("18.0")}} has been disabled on the desktop version of Firefox ({{bug(888304)}}), as some popular sites including Google and Twitter were not working properly. Once the bug is fixed, the API will be enabled again. To enable it anyway, open <code>about:config</code> and set the <code>dom.w3c_touch_events.enabled</code> preference to <code>2</code>. The mobile versions including <a href="/en-US/docs/Mozilla/Firefox_for_Android">Firefox for Android</a> and <a href="/en-US/docs/Mozilla/Firefox_OS">Firefox OS</a> are not affected by this change.</p>
<p>[2] As of Gecko 52.0, touch events support has been fixed and reenabled in Windows desktop platforms.</p>
<div class="note">
<p><strong>Note</strong>: Prior to Gecko 6.0 {{geckoRelease("6.0")}}, Gecko offered a <a href="/en-US/docs/Web/Guide/Events/Touch_events_(Mozilla_experimental)">proprietary touch event API</a>. That API is now deprecated; you should switch to this one.</p>
</div>
<h3 id="Firefox_touch_events_and_multiprocess_(e10s)">Firefox, touch events, and multiprocess (e10s)</h3>
<p>In Firefox, touch events are disabled when e10s (electrolysis; <a href="/en-US/docs/Mozilla/Firefox/Multiprocess_Firefox">multiprocess Firefox</a>) is disabled. e10s is on by default in Firefox, but can end up becoming disabled in certain situations, for example when certain accessibility tools or Firefox add-ons are installed that require e10s to be disabled to work. This means that even on a touchscreen-enabled desktop/laptop, touch events won't be enabled.</p>
<p>You can test whether e10s is disabled by going to <code>about:support</code> and looking at the "Multiprocess Windows" entry in the "Application Basics" section. 1/1 means it is enabled, 0/1 means disabled.</p>
<p>If you want to force e10s to be on — to explicitly reenable touch events support — you need to go to <code>about:config</code> and create a new boolean preference <code>browser.tabs.remote.force-enable</code>. Set it to <code>true</code>, restart the browser, and e10s will be enabled regardless of any other settings.</p>
|