JavaScriptでuserAgentを取得してみた

# はじめに

ユーザーがモッピーの推奨環境にアクセスできているかどうか
確認ができるページがほしいという要望があった。
開発時に学びがあったこと、つまづいたいたことがあったので、備忘録としてまとめることにした。


## そもそも
さっそく要望にそって開発しようと思ったが、ここで疑問が２つあった。

1. **推奨環境の細かく決まってない部分をどうしようか？**
1. **閲覧環境情報を取得するにはどうしたらいいのだろう？**

### 1について
当時プロダクトの推奨ブラウザのみは決まっていたが、
その中にはサポート対象外となったIE11も含まれていた。
策定時期も古かったため全体的に見直し、推奨環境の策定をするところからはじめた。

今回推奨環境と決まったのが以下（決めた経緯については割愛）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379825/4c7cdaf7-c972-c721-6c00-32cced530d3f.png" width="600">

### 2について

調べていくうちに、JavaScriptの`windows.navigator`オブジェクトで用意されている`userAgent`プロパティを参照すれば、
閲覧環境情報を取得できることがわかった。

早速実装してみることに。

# 実装開始

デザインはこちら。
閲覧環境によって、有効か無効かを✔か✘を出し分けし、
推奨環境で閲覧できているか視覚的にわかるようにする。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379825/acd777bf-e6b2-4568-4aae-f077cbfc6777.png" width="600">

※Operaブラウザは推奨環境外なので、ブラウザ部分が✘表示になる。
※利用端末とCookie、Javascriptの有効無効は今回のユーザーエージェントの部分とは別になるので、本記事では割愛

## 下準備：userAgentの取得

まずは自分の閲覧環境情報を取得するために、エディタ上に以下のコードを入力し、コンソール上に表示させてみる。

```Javascript:JavaScript
console.log(window.navigator.userAgent.toLowerCase())
```

```C++:コンソール出力結果
mozilla/5.0 (macintosh; intel mac os x 10_15_7) applewebkit******* chrome/107.0.0.0 safari/537.36
```

自分の閲覧環境情報の出力に成功！

私はMacOSのChromeブラウザで閲覧しているので、
`macintosh; intel mac os x 10_15_7` `chrome/107.0.0.0`と間違いなく取得できていることを確認。

備忘録として、、、
JavaScriptの`toLowerCase()`は文字列をすべて小文字にするもの。
なくても開発上は問題ないが、取得した文字列が大文字小文字混在するのが気持ち悪いのと、タイピング時エラーの元となりそうなため、
`toLowerCase()`をつけて取得した。


## OS分岐編

OSは以下3つの分岐で対応。

- パソコンの場合
  - Windows端末
  - iOS端末
  - それ以外

- スマートフォンの場合
  - Android端末
  - iOS端末
  - それ以外

```Javascript:PCの場合
var uaOs = window.navigator.userAgent.toLowerCase();

if (uaOs.indexOf("windows nt") !== -1) {
    // Windowsの場合
    console.log("Windowsだよ！")
} else if (uaOs.indexOf("mac os x") !== -1) {
    // macOSの場合
    console.log("Macだよ！")
} else {
    // 上記以外のOS
    console.log("Linuxとかですか…？")
}
```
※SPのコードは割愛

実際のものより簡易なコードにしたが、これで条件分岐が可能。

`indexOf`メソッドで、取得したユーザーエージェントの文字列を検索し、
`indexOf`の値に、ユーザー情報とマッチさせたい値をいれるだけ！

例えば、Mac端末なら、先程出力したコンソール結果の中に`mac os x`があるので、
`.indexOf("mac os x")`とすると、Mac端末を利用しているユーザーのみの処理が行えるということ。

### つまづいたこと

この調子でOSのバージョンまで取得しようと思い、さらにセグメントさせてみたがなぜか反映されない。。。
そこで現バージョンとuserAgentで取得される情報を比べてみたら、

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379825/df1c9d52-ba47-a59d-38b8-6ad694270132.png" width="600">

*なんか・・・違う！！！*

OSバージョンは`12.5.1`のはずなのに、userAgentで取得される情報は`10.15.7`・・・

ちなみに最近Windows11にアップデートしたメンバーの端末でも試してみたが、その場合も
OSバージョンは`Windows NT 11.0`のはずなのに、userAgentで取得される情報は`Windows NT 10.0`・・・

参考文献を漁ってみるも、有益な情報が得られず結局原因不明。
最初に購入して入れたOSバージョンが、userAgentには死ぬまで残り続けてしまうのではと結論づけた（推測）

またもうひとつの懸念として、今後最新OSバージョンが出るたびにバージョンの値を追加しなければならない運用の問題も出た。
（`indexOf`メソッドで値をマッチさせるため、10.0以上という書き方ができない)
OSについてはバージョンのセグメントまでは行わず、WindowsかMacのざっくり分岐に留めることとなった。

## ブラウザ分岐編

OSの条件分岐が終わったので、ブラウザの分岐にも取り掛かる。

PCは推奨ブラウザを複数用意しているので、比較的簡単に分岐させることができた。

OSごとに推奨ブラウザが異なるので、先ほどのOS分岐の中に更にif文を書いていく。

```Javascript:PCの場合
var uaOs = window.navigator.userAgent.toLowerCase();
var uaBrowser = window.navigator.userAgent.toLowerCase();

if (uaOs.indexOf("windows nt") !== -1) {
    // Windowsの場合
    if (uaBrowser.indexOf("edg") != -1 || uaBrowser.indexOf("edge") != -1) {
        console.log("Edgeは有効だよ！")
    } else if (uaBrowser.indexOf("opr") != -1 || uaBrowser.indexOf("opera") != -1) {
        console.log("Operaは無効だよ。。")
    } else if (uaBrowser.indexOf("chrome") != -1 || uaBrowser.indexOf("firefox") != -1) {
        console.log("chrome/firefoxは有効だよ！")
    } else {
        console.log("何を使ってるか知らないけど無効だよ。。")
    }
} else if (uaOs.indexOf("mac os x") !== -1) {
    // macOSの場合
    console.log("Mac用の分岐を同様に入れていくよ！")
} else {
    // 上記以外の場合
    console.log("OSは対象外だけどchromeでの閲覧はOKにしようかな？")
}
```

PCの場合はこれでOK。

### つまづいたこと

この調子でSPのブラウザ分岐もしようと思っていたが、
iOS端末での分岐がどうしてもうまくいかない。

iOS端末での閲覧条件は、**Safariブラウザのみを推奨**としているが、
chromeブラウザで閲覧しても有効になってしまう事象が発生していた。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379825/1dc81b5b-b9f3-6bb2-4029-4ae93dda623b.png" width="300">

*※iOS端末の場合、Chromeで閲覧しているならブラウザのところが✘になってないとおかしい・・・*

iOS実機のChromeでユーザーエージェント情報をテキストで出してみると…

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379825/acda1115-5c2f-de3e-a8fe-1d1152176b57.png" width="400">

確かにChromeで見ているのに、末尾に`safari`の識別子が！！！

しかも、iOS端末の場合は`chrome`ではなく、`crios`という識別子になるということが判明。

どうやらiOSだけ勝手が違うようで、以下のコードにすることで解決。

```javascript:SPの完成したコード
var uaOs = window.navigator.userAgent.toLowerCase();
var uaBrowser = window.navigator.userAgent.toLowerCase();

if(uaOs.indexOf("iphone") !== -1) {
	// iOSの場合
	if (uaBrowser.indexOf("edg") != -1 || uaBrowser.indexOf("edge") != -1) {
		console.log("Edgeは有効だよ！")
	} else if (uaBrowser.indexOf("safari") != -1) {
		// iOSはSafariの識別子が必ず入ってしまうので、更に分岐させる必要がある
		if (uaBrowser.indexOf("crios") != -1 || uaBrowser.indexOf("fxios") != -1) {
			console.log("chrome/firefoxは無効だよ。。")
		} else {
			console.log("safariは有効だよ！")
		}
	} else {
		console.log("何を使ってるか知らないけど無効だよ。。")
	}
}
```

無事にChrome閲覧時は無効になるようになった。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379825/5868aec0-98b5-7b85-0e62-cd6e9232746e.png" width="300">

## まとめ

- userAgentプロパティは便利だけど、取得できる情報や識別子が端末によってばらつきがあったりと少し扱いにくかった印象。

- Chromeにおいてはユーザーエージェントがゆくゆく廃止されてしまう（個人情報の観点で）とのことなので、
[User Agent Client Hints](https://wicg.github.io/ua-client-hints/)で判定する方法に変更したりが必要だが、
Chrome以外の他のブラウザに実装されるかどうかが不明など、変更する際はもう少し調査する必要がありそう。