JavaScriptによるタッチデバイスの検出
タッチ検出は一見シンプルに思えますが、Surface Pro、Magic Keyboardを装着したiPad、タッチスクリーン搭載のChromebookなどを目の前にすると話は変わります。これらのハイブリッドデバイスは、「タッチ」と「マウス」が相互排他的であるという前提を覆します。そして、まさにそこで多くの検出アプローチが破綻するのです。
本記事では、実際に機能する方法、機能しない方法、そしてタッチ検出をそもそも行うべきでない場合について、本質を突いた解説を提供します。
重要なポイント
'ontouchstart' in windowは信頼性に欠けます。ブラウザがこのプロパティを公開する方法に一貫性がなく、タッチスクリーンを持たないデバイスでも検出されることがあります。navigator.maxTouchPoints > 0は、モダンブラウザ全体で報告されるタッチ機能を確認するための最も信頼できるシンプルなJavaScriptチェックです。- CSSポインターメディアクエリ(
pointer、any-pointer、hover)は、JavaScriptを使わずにほとんどのUI適応を処理できます。 - Pointer Events APIとその
pointerTypeプロパティを使用すると、ページ読み込み時の一度きりのチェックよりもはるかに有用な、ユーザーの現在の入力方法を検出できます。特にハイブリッドデバイスで効果的です。
なぜ'ontouchstart' in windowでは不十分なのか
長年、開発者は'ontouchstart' in windowをタッチ機能を検出する手軽な方法として使用してきました。問題は、ブラウザがこのプロパティを一貫性なく公開していることです。Windows 8以降の一部のデスクトップブラウザでは、タッチスクリーンがなくてもtrueを返します。Chromeはバージョン間でその動作を変更してきました。これは信頼性の低いシグナルです。
ontouchstartだけに依存することは、実際のハードウェア機能ではなく、ブラウザの動作を検出していることを意味します。
navigator.maxTouchPoints: より信頼性の高いベースライン
現在、タッチサポートを検出するための最も信頼できるJavaScriptプロパティはnavigator.maxTouchPointsです。これは、デバイスがサポートする同時タッチ接触点の最大数を返します。ゼロより大きい値は、ハードウェアがタッチ機能を報告していることを意味します。
function hasTouchSupport() {
return navigator.maxTouchPoints > 0
}
これはPointer Events仕様の一部であり、Chrome、Firefox、Safari、Edgeなど、すべてのモダンブラウザでサポートされています。クリーンで読みやすく、イベントハンドラーのスニッフィングに依存しません。
注:
navigator.msMaxTouchPointsはIE時代の同等のプロパティでした。レガシーコードを保守している場合を除き、これは不要です。
CSSポインターメディアクエリがより優れたツールである場合
ほとんどのUI適応では、JavaScriptは全く必要ありません。CSSポインターメディアクエリを使用すると、主要な入力デバイスの精度に基づいてスタイリングを調整できます。
/* 主要なポインターが粗い(例:指)デバイスをターゲットにする */
@media (pointer: coarse) {
.btn {
min-height: 48px;
}
}
/* セカンダリ入力を含む、粗いポインターが利用可能なデバイスをターゲットにする */
@media (any-pointer: coarse) {
.tooltip {
display: none;
}
}
/* ホバーが確実に利用できないデバイスをターゲットにする */
@media (hover: none) {
.dropdown:hover .menu {
display: none;
}
}
pointerとany-pointerの違いは、ハイブリッドデバイスで重要です。pointer: coarseは主要な入力のみを反映します。any-pointer: coarseは、接続されている入力のいずれかが粗い場合にtrueを返します。これは、デバイスがマウスとタッチスクリーンの両方を持っている場合に便利です。
Discover how at OpenReplay.com.
Pointer Events vs. Touch Events
Pointer Eventsは、JavaScriptでマウス、タッチ、スタイラス入力を処理するための、モダンで統一されたモデルです。touchstartとmousedownの別々のハンドラーを維持する代わりに、1つだけ記述します:
element.addEventListener('pointerdown', (e) => {
if (e.pointerType === 'touch') {
// タッチ入力を処理
} else if (e.pointerType === 'mouse') {
// マウス入力を処理
}
})
pointerTypeプロパティは、デバイスが理論的に何ができるかではなく、ユーザーが今まさに何をしているかを正確に教えてくれます。これは、ハイブリッドデバイスで最も重要な区別です。
レガシーなTouch Events(touchstart、touchmove、touchend)は、ほとんどのブラウザでまだサポートされていますが、すべての環境で利用できるわけではなく、マウスやスタイラス入力をカバーしていません。新しいコードではPointer Eventsを優先してください。
ハイブリッドデバイスの問題
タッチ対応ラップトップのユーザーは、マウスでセッションを開始し、その後手を伸ばして画面をタップするかもしれません。ページ読み込み時に行った検出は、すでに古くなっています。
単一の検出結果に固執するのではなく、pointerdownイベントをリッスンし、e.pointerTypeを動的に読み取ります。これにより、デバイスが理論的にサポートしているものではなく、ユーザーが実際に行っていることに基づいてUIを適応させることができます。
何をいつ使用するか
| 目的 | 推奨アプローチ |
|---|---|
| ボタンサイズやタップターゲットの調整 | CSS @media (pointer: coarse) |
| ホバー専用UIの抑制 | CSS @media (hover: none) |
| JSでタッチハードウェア機能を確認 | navigator.maxTouchPoints > 0 |
| すべてのタイプの入力イベントを処理 | Pointer Events API |
| 現在の入力タイプを動的に検出 | pointerdownでのevent.pointerType |
結論
ユーザーエージェントスニッフィングは完全にスキップしてください。UI調整については、CSSポインターメディアクエリがJavaScriptを1行も書かずにほとんどのケースを処理します。JavaScriptが必要な場合、navigator.maxTouchPointsは信頼性の高いハードウェアシグナルを提供し、Pointer Events APIはリアルタイムの入力コンテキストを提供します。これらを組み合わせることで、シンプルな検出を信頼できないものにするハイブリッドデバイスを含む、モダンデバイスの全範囲をカバーできます。
よくある質問
はい。navigator.maxTouchPointsはPointer Events仕様の一部であり、Chrome、Firefox、Safari、Edgeでサポートされています。タッチサポートを報告しないデバイスでは通常ゼロを返し、タッチ対応デバイスでは正の整数を返します。現在、JavaScriptでタッチサポートを検出するための最も信頼できる単一プロパティチェックです。
ユーザーエージェント文字列は、入力機能を判断するには信頼性に欠けます。これらはブラウザとオペレーティングシステムを識別するものであり、ハードウェアを識別するものではありません。WindowsラップトップとWindowsタブレットは、入力方法が大きく異なるにもかかわらず、同じユーザーエージェント文字列を共有できます。maxTouchPointsやCSSメディアクエリによる機能検出の方がはるかに正確です。
はい。Pointer Events APIは、pointerdownなどのイベントでpointerTypeプロパティを提供します。その値は、その瞬間に使用されている入力に応じて、touch、mouse、またはpenになります。これは、ユーザーが入力を切り替えるハイブリッドデバイスでは特に、一度きりの機能チェックよりも有用です。
pointerメディアクエリは主要な入力デバイスのみをターゲットにします。any-pointerクエリは、利用可能な入力のいずれかが条件に一致する場合にtrueを返します。トラックパッドとタッチスクリーンの両方を持つラップトップでは、トラックパッドが主要であるためpointer coarseはfalseかもしれませんが、タッチスクリーンが該当するためany-pointer coarseはtrueになります。
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Check our GitHub repo and join the thousands of developers in our community.