JavaScript: ブラウザを開いているのがPCかモバイルかを切り分ける ー とくにタッチ操作に絡む場合


# 01 User-Agentを使う
ブラウザを開いているのがWindows PCやMacなのか、モバイル端末なのかは[`window.navigator.userAgent`](https://developer.mozilla.org/docs/Web/API/NavigatorID/userAgent)で調べられます。[User-Agent](https://developer.mozilla.org/docs/Web/HTTP/Headers/User-Agent)はつぎのような構文の文字列です。

```
<product> / <product-version> <comment>
```

webブラウザの一般的な書式はつぎのとおりで、`platform`がオペレーティングシステム(OS)を示します。PCあるいはMacの場合に含まれる文字列は、それぞれ[`Windows`](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/User-Agent/Firefox#Windows)と[`Macintosh`](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/User-Agent/Firefox#Macintosh)です。[`Linux`](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/User-Agent/Firefox#Linux)の場合はモバイルもあり得ますので、もう少し細かく調べなければなりません。本稿では省くことにします。

```
Mozilla/<version> (<system-information>) <platform> (<platform-details>) <extensions>
```

確かめ方としては、`navigator.userAgent`の戻り値に`platform`の文字列が含まれるか調べればよいでしょう。

```js
const agent = navigator.userAgent.toLowerCase();
if (agent.includes('windows')) {
	console.log('Windows');
} else if (agent.includes('macintosh')) {
	console.log('Mac');
} else {
	console.log('mobile');
}
```

もっとも、MDNのリファレンスには、つぎのような脅迫めいた「[注記](https://developer.mozilla.org/ja/docs/Web/API/NavigatorID/userAgent#.E6.B3.A8.E8.A8.98)」が加えられています。

> ユーザエージェントを表す文字列を検出することに基づいたブラウザ識別は、信頼できないものであり、また、推奨されません。なぜなら、ユーザエージェントを表す文字列は、ユーザによって変更することができるからです。


# 02 判定に同時タッチコンタクト数を加える
あえてユーザエージェントを書き替えるユーザーが多いとも思えません。けれど、保険をかけるに越したことはないでしよう。そこで使ったのが、[`Navigator.maxTouchPoints`](https://developer.mozilla.org/ja/docs/Web/API/Navigator/maxTouchPoints)です。タッチパネルがなければ、数値0が戻り値です。タッチパネルが備わっていないLinuxデスクトップも拾えます。

つぎのようなサンプルコードを書いてみました。併せて、試していただけるよう、CodePenにも掲げます(サンプル001)。

```js
const info = getDeviceInfo();
const result = (info.windows || info.mac || info.touchPoints === 0) ? 'PC!' : 'mobile';
console.log(result);
function getDeviceInfo() {
	const agent = navigator.userAgent.toLowerCase();
	const touchPoints = ('maxTouchPoints' in navigator) ? navigator.maxTouchPoints : 0;
	const result = {
		windows: agent.includes('windows'),
		mac: agent.includes('macintosh'),
		linux: agent.includes('linux'),
		touchPoints: touchPoints
	}
	return result;
}
```

#### サンプル001■JavaScript: Evaluating PC or mobile
<p class="codepen" data-height="265" data-theme-id="default" data-default-tab="js,result" data-user="FumioNonaka" data-slug-hash="QWWPGNK" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="JavaScript: Evaluating PC or mobile">
  <span>See the Pen <a href="https://codepen.io/FumioNonaka/pen/QWWPGNK">
  JavaScript: Evaluating PC or mobile</a> by Fumio Nonaka (<a href="https://codepen.io/FumioNonaka">@FumioNonaka</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

なお、今回はタッチ操作に関わってPCとモバイルを切り分けました。画面サイズやレイアウトを考えるときは、また少し違ってくるでしょう。ユーザーエージェントに頼らない手法については、MDN「[ユーザーエージェント文字列を用いたブラウザーの判定](https://developer.mozilla.org/docs/Web/HTTP/Browser_detection_using_the_user_agent)」が参考になります。たとえば、以下の引用は画面サイズの場合です。

> 画面のサイズについては、`window.innerWidth`と`window.addEventListener("resize", function(){ /*refresh screen size dependent things*/ })`を使用するだけです。

[注記: 2019/11/29] 文字列が含まれるかどうか調べるメソッドを、`indexOf()`からECMAScript 2015の`includes()`に差し替えた。