Skip to content

Instantly share code, notes, and snippets.

@myakura
Created June 11, 2014 10:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save myakura/1e43ce64eeaa34e691e3 to your computer and use it in GitHub Desktop.
Save myakura/1e43ce64eeaa34e691e3 to your computer and use it in GitHub Desktop.
[Incomplete] Translation for the Mozilla Hacks Article on Touch Detection
タッチの検出:「どう」ではなく「なぜ」
<p>原文:<a href="https://hacks.mozilla.org/2013/04/detecting-touch-its-the-why-not-the-how/">Detecting touch: it’s the ‘why’, not the ‘how’</a> by Patrick H. Lauke and Robert Nyman [Editor] on April 9, 2013</p>
<p>One common aspect of making a website or application “mobile friendly” is the inclusion of tweaks, additional functionality or interface elements that are particularly aimed at touchscreens. A very common question from developers is now “How can I detect a touch-capable device?”</p>
<p>Web サイトや Web アプリケーションをモバイル対応するにあたり、共通して見られるaspectのひとつに、タッチスクリーンを対象にした機能追加や対処があります。こういったこともあり、開発者から「どうやってタッチ可能なデバイスを検出するの?」という質問がよく投げられます。</p>
<h2>タッチ機能の検出
Feature detection for touch</h2>
<p>Although there used to be a few incompatibilities and proprietary solutions in the past (such as <a href="https://developer.mozilla.org/en-US/docs/DOM/Touch_events_(Mozilla_experimental)">Mozilla’s experimental, vendor-prefixed event model</a>), almost all browsers now implement the same <a href="http://www.w3.org/TR/touch-events/">Touch Events</a> model (based on a solution first introduced by Apple for iOS Safari, which subsequently was adopted by other browsers and retrospectively turned into a W3C draft specification).</p>
<p>過去を振り返れば、非互換でプロプライエタリなもの(たとえば<a href="https://developer.mozilla.org/en-US/docs/DOM/Touch_events_(Mozilla_experimental)">Mozilla の試験的でベンダー接頭辞つきのイベントモデル</a>など)がありましたが、現在はほとんどすべてのブラウザが、<a href="http://www.w3.org/TR/touch-events/">Touch Events</a> モデルを実装しています(これは Apple が iOS Safari に追加したものが他のブラウザベンダーにも取り入れられた後、W3C 仕様となったものです)。</p>
<p>As a result, being able to programmatically detect whether or not a particular browser supports touch interactions involves a very simple feature detection:</p>
<p>結果として、タッチインターフェースを備えたブラウザの検出は、実質的にとても簡単な機能検出でできるようになりました。</p>
<div class="wp_syntax"><table><tbody><tr><td class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">(</span><span style="color: #3366CC;">'ontouchstart'</span> <span style="color: #000066; font-weight: bold;">in</span> window<span style="color: #009900;">)</span> <span style="color: #009900;">{</span>
<span style="color: #006600; font-style: italic;">/* browser with Touch Events
running on touch-capable device */</span>
<span style="color: #009900;">}</span></pre></td></tr></tbody></table></div>
<pre>if ('ontouchstart' in window) {
/* browser with Touch Events
running on touch-capable device */
}</pre>
<p>This snippet works reliably in modern browser, but older versions notoriously had a few quirks and inconsistencies which required jumping through various different detection strategy hoops. If your application is targetting these older browsers, I’d recommend having a look at <a href="http://modernizr.com">Modernizr</a> – and in particular its various <a href="http://modernizr.github.com/Modernizr/touch.html">touch test approaches</a> – which smooths over most of these issues.</p>
<p>このコードはモダンブラウザで問題なく動作しますが、古いブラウザはnotoriouslyにいくつかのquirksや一貫しない挙動があるため、いくつかの異なるstretegy hoopsを必要とします。あなたのアプリが古いブラウザも対象としている場合は、<a href="http://modernizr.com">Modernizr</a> の<a href="http://modernizr.github.com/Modernizr/touch.html">タッチ検出テストの方法</a>を見てみてください。この方法によって、ほとんどの問題に対処できると思います。</p>
<p>I noted above that “almost all browsers” support this touch event model. The big exception here is Internet Explorer. While up to IE9 there was no support for any low-level touch interaction, IE10 introduced support for Microsoft’s own <a href="http://www.w3.org/Submission/pointer-events/">Pointer Events</a>. This event model – which has since been submitted for W3C standardisation – unifies “pointer” devices (mouse, stylus, touch, etc) under a single new class of events. As this model does not, by design, include any separate ‘touch’, the feature detection for <code>ontouchstart</code> will naturally not work. The suggested method of detecting if a browser using Pointer Events is running on a touch-enabled device instead involves checking for the existence and return value of <code>navigator.maxTouchPoints</code> (note that Microsoft’s Pointer Events are currently still vendor-prefixed, so in practice we’ll be looking for <code>navigator.msMaxTouchPoints</code>). If the property exists and returns a value greater than <code>0</code>, we have touch support.</p>
<p>先ほど、「ほとんどすべてのブラウザ」がこのイベントモデルをサポートしていると書きました。ただし大きな例外があります。Internet Explorer です。IE9 までは低レベルのタッチインタラクションのサポートはされていませんでしたが、IE10 では Microsoft 独自の <a href="http://www.w3.org/Submission/pointer-events/">Pointer Events</a> を採用しています(その後 W3C での標準化を目的に提出されています)。このイベントモデルは「ポインタ」デバイス(マウス、スタイラス、タッチ、など)をひとつのクラスとして扱います。この設計上の理由から、このモデルには「タッチ」という個別の概念がありません。ですから <code>ontouchstart</code> による機能検出は使えません。代わりの検出方法として、Pointer Events をサポートするブラウザがタッチ可能なデバイス上で動作している場合、<code>navigator.maxTouchPoints</code> の存在とそのプロパティが返す値をチェックするというのがあります(Microsoft の Pointer Events はベンダー接頭辞がついているため、実際には <code>navigator.msMaxTouchPoints</code> を確かめることになります)。プロパティが存在しその返り値が <code>0</code> よりも大きい場合、タッチをサポートしていると判断できます。</p>
<p>訳注:Pointer Events は W3C の仕様として正式に標準化が進められ、現在は勧告候補まで達しています。Google, Mozilla からも賛同を得ており、Blink, Gecko で実装が進められています。</p>
<div class="wp_syntax"><table><tbody><tr><td class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">(</span>navigator.<span style="color: #660066;">msMaxTouchPoints</span> <span style="color: #339933;">&gt;</span> <span style="color: #CC0000;">0</span><span style="color: #009900;">)</span> <span style="color: #009900;">{</span>
<span style="color: #006600; font-style: italic;">/* IE with pointer events running
on touch-capable device */</span>
<span style="color: #009900;">}</span></pre></td></tr></tbody></table></div>
<p>Adding this to our previous feature detect – and also including the non-vendor-prefixed version of the Pointer Events one for future compatibility – we get a still reasonably compact code snippet:</p>
<p>このコードを先ほどの機能検出に加え、さらに前方互換のため接頭辞なしのプロパティも含めたとしても、なおコンパクトに収まります。</p>
<div class="wp_syntax"><table><tbody><tr><td class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">(</span><span style="color: #009900;">(</span><span style="color: #3366CC;">'ontouchstart'</span> <span style="color: #000066; font-weight: bold;">in</span> window<span style="color: #009900;">)</span> <span style="color: #339933;">||</span>
<span style="color: #009900;">(</span>navigator.<span style="color: #660066;">maxTouchPoints</span> <span style="color: #339933;">&gt;</span> <span style="color: #CC0000;">0</span><span style="color: #009900;">)</span> <span style="color: #339933;">||</span>
<span style="color: #009900;">(</span>navigator.<span style="color: #660066;">msMaxTouchPoints</span> <span style="color: #339933;">&gt;</span> <span style="color: #CC0000;">0</span><span style="color: #009900;">)</span><span style="color: #009900;">)</span> <span style="color: #009900;">{</span>
<span style="color: #006600; font-style: italic;">/* browser with either Touch Events of Pointer Events
running on touch-capable device */</span>
<span style="color: #009900;">}</span></pre></td></tr></tbody></table></div>
<h2>How touch detection is used
タッチ検出はどう使われるか</h2>
<p>Now, there are already quite a few commonly-used techniques for “touch optimisation” which take advantage of these sorts of feature detects. The most common use cases for detecting touch is to increase the responsiveness of an interface for touch users.</p>
<p>こうした機能検出を利用した「タッチ最適化」のテクニックが幾つか存在します。タッチ検出をする最も大きな理由の1つに、タッチの反応速度を高めるというものがあります。</p>
<p>When using a touchscreen interface, browsers introduce an artificial delay (in the range of about 300ms) between a touch action – such as tapping a link or a button – and the time the actual click event is being fired.</p>
<p>タッチスクリーンインターフェースを使用する際、ブラウザはタッチアクション間に意図的にdelay(300msほど)を挿入します。たとえばリンクやボタンをタップしてから click イベントが発火する間にそうしたdelayがあります。</p>
<p>More specifically, in browsers that support Touch Events the delay happens between <code>touchend</code> and the simulated mouse events that these browser also fire for compatibility with mouse-centric scripts:</p>
<p><code>touchstart &gt; [touchmove]+ &gt; touchend &gt; <strong> delay </strong> &gt; mousemove &gt; mousedown &gt; mouseup &gt; click</code></p>
<p>より具体的に言うと、Touch Events をサポートするブラウザでは、 <code>touchend</code> から、マウスだけをターゲットにしたスクリプトとの互換性をとるため発火されるsimulatedなマウスイベントの間にdelayが発生します。</p>
<p><code>touchstart &gt; [touchmove]+ &gt; touchend &gt; <strong> delay </strong> &gt; mousemove &gt; mousedown &gt; mouseup &gt; click</code></p>
<p class="note">See the <a href="http://mozilla.github.io/mozhacks/touch-events/event-listener.html">event listener test page</a> to see the order in which events are being fired, <a href="https://github.com/mozilla/mozhacks/blob/gh-pages/touch-events/event-listener.html">code available on GitHub</a>.</p>
<p>This delay has been introduced to allow users to double-tap (for instance, to zoom in/out of a page) without accidentally activating any page elements.</p>
<div class="note">
<p>It’s interesting to note that Firefox and Chrome on Android have removed this delay for pages with a fixed, non-zoomable viewport.</p>
<pre>&lt;meta name="viewport" value="... <strong>user-scalable = no</strong> ..."&gt;</pre>
<p>See the <a href="http://mozilla.github.io/mozhacks/touch-events/event-listener_user-scalable-no.html">event listener with <code>user-scalable=no</code> test page</a>, <a href="https://github.com/mozilla/mozhacks/blob/gh-pages/touch-events/event-listener_user-scalable-no.html">code available on GitHub</a>.</p>
<p>There is some discussion of tweaking Chrome’s behavior further for other situations – see <a href="https://code.google.com/p/chromium/issues/detail?id=169642">issue 169642 in the Chromium bug tracker</a>.</p>
</div>
<p>Although this affordance is clearly necessary, it can make a web app feel slightly laggy and unresponsive. One common trick has been to check for touch support and, if present, react directly to a touch event (either <code>touchstart</code> – as soon as the user touches the screen – or <code>touchend</code> – after the user has lifted their finger) instead of the traditional <code>click</code>:</p>
<div class="wp_syntax"><table><tbody><tr><td class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #006600; font-style: italic;">/* if touch supported, listen to 'touchend', otherwise 'click' */</span>
<span style="color: #000066; font-weight: bold;">var</span> clickEvent <span style="color: #339933;">=</span> <span style="color: #009900;">(</span><span style="color: #3366CC;">'ontouchstart'</span> <span style="color: #000066; font-weight: bold;">in</span> window <span style="color: #339933;">?</span> <span style="color: #3366CC;">'touchend'</span> <span style="color: #339933;">:</span> <span style="color: #3366CC;">'click'</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
blah.<span style="color: #660066;">addEventListener</span><span style="color: #009900;">(</span>clickEvent<span style="color: #339933;">,</span> <span style="color: #000066; font-weight: bold;">function</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span> <span style="color: #009900;">{</span> ... <span style="color: #009900;">}</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span></pre></td></tr></tbody></table></div>
<p>Although this type of optimisation is now widely used, it is based on a logical fallacy which is now starting to become more apparent.</p>
<div class="note">
<p>The artificial delay is also present in browsers that use Pointer Events.</p>
<p><code>pointerover &gt; mouseover &gt; pointerdown &gt; mousedown &gt; pointermove &gt; mousemove &gt; pointerup &gt; mouseup &gt; pointerout &gt; mouseout &gt; <strong>delay</strong> &gt; click</code></p>
<p>Although it’s possible to extend the above optimisation approach to check <code>navigator.maxTouchPoints</code> and to then hook up our listener to <code>pointerup</code> rather than <code>click</code>, there is a much simpler way: setting the <a href="http://www.w3.org/Submission/pointer-events/#the-touch-action-css-property"><code>touch-action</code> CSS property</a> of our element to <code>none</code> eliminates the delay.</p>
<div class="wp_syntax"><table><tbody><tr><td class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #006600; font-style: italic;">/* suppress default touch action like double-tap zoom */</span>
a<span style="color: #339933;">,</span> button <span style="color: #009900;">{</span>
<span style="color: #339933;">-</span>ms<span style="color: #339933;">-</span>touch<span style="color: #339933;">-</span>action<span style="color: #339933;">:</span> none<span style="color: #339933;">;</span>
touch<span style="color: #339933;">-</span>action<span style="color: #339933;">:</span> none<span style="color: #339933;">;</span>
<span style="color: #009900;">}</span></pre></td></tr></tbody></table></div>
<p>See the <a href="http://mozilla.github.io/mozhacks/touch-events/event-listener_touch-action-none.html">event listener with <code>touch-action:none</code> test page</a>, <a href="https://github.com/mozilla/mozhacks/blob/gh-pages/touch-events/event-listener_touch-action-none.html">code available on GitHub</a>.</p>
</div>
<h2>False assumptions</h2>
<p>It’s important to note that these types of optimisations based on the availability of touch have a fundamental flaw: they make assumptions about user behavior based on device capabilities. More explicitly, the example above assumes that because a device is capable of touch input, a user will in fact use touch as the only way to interact with it.</p>
<p>This assumption probably held some truth a few years back, when the only devices that featured touch input were the classic “mobile” and “tablet”. Here, touchscreens were the only input method available. In recent months, though, we’ve seen a whole new class of devices which feature both a traditional laptop/desktop form factor (including a mouse, trackpad, keyboard) <em>and</em> a touchscreen, such as the various <a href="http://windows.microsoft.com/en-us/windows-8/new-pcs">Windows 8 machines</a> or <a href="http://www.google.com/intl/en_uk/chrome/devices/chromebook-pixel/">Google’s Chromebook Pixel</a>.</p>
<div class="note">
<p>As an aside, even in the case of mobile phones or tablets, it was already possible – on some platforms – for users to add further input devices. While iOS only caters for pairing an additional bluetooth keyboard to an iPhone/iPad purely for text input, Android and Blackberry OS also let users add a mouse.</p>
<p>On Android, this mouse will act exactly like a “touch”, even firing the same sequence of touch events and simulated mouse events, including the dreaded delay in between – so optimisations like our example above will still work fine. Blackberry OS, however, purely fires mouse events, leading to the same sort of problem outlined below.</p>
</div>
<p>The implications of this change are slowly beginning to dawn on developers: that touch support does not necessarily mean “mobile” anymore, and more importantly that even if touch is available, it may not be the primary or exclusive input method that a user chooses. In fact, a user may even transition between any of their available input methods in the course of their interaction.</p>
<p>The innocent code snippets above can have quite annoying consequences on this new class of devices. In browsers that use Touch Events:</p>
<div class="wp_syntax"><table><tbody><tr><td class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #000066; font-weight: bold;">var</span> clickEvent <span style="color: #339933;">=</span> <span style="color: #009900;">(</span><span style="color: #3366CC;">'ontouchstart'</span> <span style="color: #000066; font-weight: bold;">in</span> window <span style="color: #339933;">?</span> <span style="color: #3366CC;">'touchend'</span> <span style="color: #339933;">:</span> <span style="color: #3366CC;">'click'</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span></pre></td></tr></tbody></table></div>
<p>is basically saying “if the device support touch, only listen to <code>touchend</code> and not <code>click</code>” – which, on a multi-input device, immediately shuts out any interaction via mouse, trackpad or keyboard.</p>
<h2>Touch <em>or</em> mouse?</h2>
<p>So what’s the solution to this new conundrum of touch-capable devices that may also have other input methods? While some developers have started to look at complementing a touch feature detection with additional user agent sniffing, I believe that the answer – as in so many other cases in web development – is to accept that we can’t fully detect or control how our users will interact with our web sites and applications, and to be input-agnostic. Instead of making assumptions, our code should cater for all eventualities. Specifically, instead of making the decision about whether to react to <code>click</code> or <code>touchend</code>/<code>touchstart</code> mutually exclusive, these should all be taken into consideration as complementary.</p>
<p>Certainly, this may involve a bit more code, but the end result will be that our application will work for the largest number of users. One approach, already familiar to developers who’ve strived to make their mouse-specific interfaces also work for keyboard users, would be to simply “double up” your event listeners (while taking care to prevent the functionality from firing twice by stopping the simulated mouse events that are fired following the touch events):</p>
<div class="wp_syntax"><table><tbody><tr><td class="code"><pre class="javascript" style="font-family:monospace;">blah.<span style="color: #660066;">addEventListener</span><span style="color: #009900;">(</span><span style="color: #3366CC;">'touchend'</span><span style="color: #339933;">,</span> <span style="color: #000066; font-weight: bold;">function</span><span style="color: #009900;">(</span>e<span style="color: #009900;">)</span> <span style="color: #009900;">{</span>
<span style="color: #006600; font-style: italic;">/* prevent delay and simulated mouse events */</span>
e.<span style="color: #660066;">preventDefault</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
someFunction<span style="color: #009900;">(</span><span style="color: #009900;">)</span>
<span style="color: #009900;">}</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
blah.<span style="color: #660066;">addEventListener</span><span style="color: #009900;">(</span><span style="color: #3366CC;">'click'</span><span style="color: #339933;">,</span> someFunction<span style="color: #009900;">)</span><span style="color: #339933;">;</span></pre></td></tr></tbody></table></div>
<p>If this isn’t <abbr title="Don't Repeat Yourself">DRY</abbr> enough for you, there are of course fancier approaches, such as only defining your functions for <code>click</code> and then bypassing the dreaded delay by explicitly firing that handler:</p>
<div class="wp_syntax"><table><tbody><tr><td class="code"><pre class="javascript" style="font-family:monospace;">blah.<span style="color: #660066;">addEventListener</span><span style="color: #009900;">(</span><span style="color: #3366CC;">'touchend'</span><span style="color: #339933;">,</span> <span style="color: #000066; font-weight: bold;">function</span><span style="color: #009900;">(</span>e<span style="color: #009900;">)</span> <span style="color: #009900;">{</span>
<span style="color: #006600; font-style: italic;">/* prevent delay and simulated mouse events */</span>
e.<span style="color: #660066;">preventDefault</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
<span style="color: #006600; font-style: italic;">/* trigger the actual behavior we bound to the 'click' event */</span>
e.<span style="color: #660066;">target</span>.<span style="color: #660066;">click</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
<span style="color: #009900;">}</span><span style="color: #009900;">)</span>
blah.<span style="color: #660066;">addEventListener</span><span style="color: #009900;">(</span><span style="color: #3366CC;">'click'</span><span style="color: #339933;">,</span> <span style="color: #000066; font-weight: bold;">function</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span> <span style="color: #009900;">{</span>
<span style="color: #006600; font-style: italic;">/* actual functionality */</span>
<span style="color: #009900;">}</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span></pre></td></tr></tbody></table></div>
<p class="note">That last snippet does not cover all possible scenarios though. For a more robust implementation of the same principle, see the <a href="https://github.com/ftlabs/fastclick">FastClick</a> script from <a href="http://labs.ft.com/"><abbr title="Financial Times">FT</abbr> labs</a>.</p>
<h2>Being input-agnostic</h2>
<p>Of course, battling with delay on touch devices is not the only reason why developers want to check for touch capabilities. Current discussions – such as this issue in Modernizr about <a href="https://github.com/Modernizr/Modernizr/issues/869">detecting a mouse user</a> – now revolve around offering completely different interfaces to touch users, compared to mouse or keyboard, and whether or not a particular browser/device supports things like hovering. And even beyond JavaScript, similar concepts (<code>pointer</code> and <code>hover</code> media features) are being proposed for <a href="http://dev.w3.org/csswg/mediaqueries4/">Media Queries Level 4</a>. But the principle is still the same: as there are now common multi-input devices, it’s not straightforward (and in many cases, impossible) anymore to determine if a user is on a device that <em>exclusively</em> supports touch.</p>
<p>The more generic approach taken in Microsoft’s Pointer Events specification – which is already being scheduled for implementation in other browser such as Chrome – is a step in the right direction (though it still requires extra handling for keyboard users). In the meantime, developers should be careful not to draw the wrong conclusions from touch support detection and avoid unwittingly locking out a growing number of potential multi-input users.</p>
<h2>Further links</h2>
<ul>
<li><a href="http://www.stucox.com/blog/the-good-and-bad-of-level-4-media-queries/">The Good &amp; Bad of Level 4 Media Queries</a></li>
<li><a href="http://blogs.msdn.com/b/ie/archive/2011/10/19/handling-multi-touch-and-mouse-input-in-all-browsers.aspx">Handling Multi-touch and Mouse Input in All Browsers</a></li>
<li><a href="http://blogs.msdn.com/b/eternalcoding/archive/2013/01/16/hand-js-a-polyfill-for-supporting-pointer-events-on-every-browser.aspx">Hand.js: a polyfill for supporting pointer events on every browser</a></li>
<li><a href="http://www.html5rocks.com/en/mobile/touchandmouse/">Touch And Mouse – Together Again For The First Time</a></li>
<li><a href="http://appendto.com/blog/2013/02/prototype-chromium-build-with-support-for-ms-pointer-events/">Prototype Chromium build with support for MS Pointer Events</a></li>
<li><a href="http://webkra
uts.de/artikel/2012/einfuehrung-touch-events" lang="de" hreflang="de">Webseiten zum Anfassen</a> (in German)</li>
<li><a href="http://smus.com/mouse-touch-pointer/">Generalized Input On The Cross-Device Web</a></li>
</ul>
<style>
.note { border: 1px #999 solid; background-color: #eee; margin-bottom: 1em; padding: 1em; }
</style>
<footer class="entry-meta">
<p>Posted by <a href="https://hacks.mozilla.org/author/patrickhlauke/" title="Posts by Patrick H. Lauke" rel="author">Patrick H. Lauke</a> and <a href="https://hacks.mozilla.org/author/robertnyman/" title="Posts by Robert Nyman [Editor]" rel="author">Robert Nyman [Editor]</a>
on <time datetime="2013-04-09T05:47:59+00:00">April 9, 2013</time>
at <time datetime="UTC05:47:59+00:00">5:47 am</time></p>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment