僕が提供するBlogger用テンプレートのImaginaryへの質問で、デザインが壊れて何とかして欲しいというのがあって原因の一つにブラウザがある。サイト作成のHTMLやCSSやJavaScriptのコードの一部に未対応だったり、場合によってアップデートでバグが起きて急に反映しなくなったりもする。
ブラウザの問題はいつか対応して来ると期待されるかぎり、待つしかないけれども、その間、テンプレートが壊れているのはどうにも耐えられないとなるとコードを変えるか多少の違和感ならばブラウザを除外してやり過ごせる。
今回、JavaScriptで訪問者のブラウザをOSと共にデバイス毎に判別して一部のコードを除外する方法を調べたので、纏めておきたい。
JavaScriptでブラウザとOSを判別する
JavaScriptでブラウザが何かを取得する方法としてユーザーエージェントクライアントヒント APIのNavigator.userAgentDataプロパティを使う。
注意:判別したいブラウザが対応してなければ(Can I Use…)旧来のNavigator.userAgentプロパティを使う。
ユーザーエージェントクライアントヒント API (User-Agent Client Hints API) は、クライアントヒントを拡張し、 User-Agent レスポンスおよびリクエストヘッダー、および JavaScript API によってブラウザーとプラットフォーム情報を公開する方法を提供します。
サイトを閲覧中のユーザーエージェントのブラウザとOS(プラットフォーム)の名前を取得して判別することができる。
Navigator.userAgentDataプロパティの使い方
構文はwindow.navigator.userAgentData
、またはwindowを省略してnavigator.userAgentData
にプロパティやメソッドを使ってユーザーエージェントクライアントヒント APIのオブジェクトのインスタンスを取得できる。
現在の閲覧中のユーザーエージェントクライアントヒントの内容
※Navigator.userAgentDataプロパティで取得して表示している。
ブラウザの判別に役立つ三つのインスタンスプロパティ
- Navigator.userAgentData.brands
- ブラウザ名とヴァージョンを格納した配列を返す。
- brand:ブラウザ名
- version:ヴァージョン
- Navigator.userAgentData.mobile
- モバイルかどうかの真偽値(ブーリアン)を返す。
- Navigator.userAgentData.platform
- OS名を返す。
ブラウザ名やOS名の文字列を使って条件文のifなどにかけてコードを振り分ける。
ブラウザ名に含まれる文字列の例
- Microsoft Edge
- Google Chrome
- Opera
いつか対応すればSafariやFirefoxなどの文字列も使えるだろう。
OS名に含まれる文字列の例
- Mac OS X
- Windows NT
- Linux
- iPhone
- Android
ブラウザ名だけの条件で振り分けるプログラム
ブラウザ名だけで振り分けるならばif文でNavigator.userAgentData.brandsから取得したブラウザ名が判別したいものに当て嵌まるかどうかをinclude()メソッドにかけて調べる。
やってみるとブラウザ名の配列のどこに一般的なブラウザ名が入るかが一定ではないようなので――閲覧するブラウザやヴァージョンによって順番が異なる場合がある――Navigator.userAgentData.brandsに関して条件文にfind()メソッドを使って判別したいブラウザ名があれば見付かるところまで調べると良いと思う。
Chromeを判別するサンプルのソースコード
const browserbrands = navigator.userAgentData.brands;
if (browserbrands.find(elm => elm.brand.includes("Google Chrome"))) {
// Chromeで使用したいソースコード
} else {
// Chrome以外で使用したいソースコード
}
Navigator.userAgentData.brandsの配列をfind()メソッドにかけて個々の要素のどこかに判別したいブラウザ名が含まれているかどうかでソースコードを振り分けることができる。
ブラウザ名とOSの条件で振り分けるプログラム
ブラウザ名とOS名で振り分けるならば先ずはNavigator.userAgentDataから取得したブラウザ名だけの振り分けを行って当て嵌まるときと当て嵌まらないときのそれぞれにやはりNavigator.userAgentDataから取得したOS名が含まれるかどうかのincludes()メソッドを使ったif文を入れ子にして調べる。
WindowsのChromeを判別するサンプルのソースコード
const browserbrands = navigator.userAgentData.brands, browserplatform = navigator.userAgentData.platform;
if (browserbrands.find(elm => elm.brand.includes("Google Chrome"))) {
if (browserplatform.includes("Windows")) {
// WindowsのChromeで使用したいソースコード
} else {
// Windows以外のChromeで使用したいソースコード
}} else {
if (browserplatform.includes("Windows")) {
// WindowsのChrome以外で使用したいソースコード
} else {
// Windows以外のChrome以外で使用したいソースコード
}}
同じ名前のブラウザをパソコンとスマホで振り分けるならばOS名だけではなく、mobileインスタンスプロパティの真偽値を振り分けの条件に使っても良いと思う。
Navigator.userAgentDataプロパティは安全性を得るためはSSL暗号化通信のHTTPS接続での閲覧が前提とする場合がある。試すと通常のHTTP接続ではブラウザ名やOS名を取得できなかったので、近年は殆ど見かけないけど、HTTPS接続以外も含めるならば旧来のNavigator.userAgentプロパティを使わなくてはならない。
userAgentDataと旧来の方法の使い分けのソースコード
現在、主要なブラウザの一部がNavigator.userAgentDataプロパティに対応してないので、旧来のNavigator.userAgentプロパティを使わなくてはならないので、両方に対応するためのプログラムを紹介する。
if (navigator.userAgentData) {
// 新しいNavigator.userAgentDataプロパティによる処理
} else {
// 古いNavigator.userAgentプロパティによる処理
}
if文に「navigator.userAgentData」を入れるとブラウザがNavigator.userAgentDataプロパティに対応しているかどうかを判定できる。
Chromeは古い方のNavigator.userAgentプロパティで得られる内容をを削減するといっていて今年の四月には完了する。
情報量削減後の User-Agent には、リクエストを送信したパソコンまたはモバイルのブラウザのブランドとメジャー バージョン、プラットフォームが含まれます。それ以外のデータを利用する場合は、User-Agent Client Hints を使用して、ユーザーのデバイスや状態に関する特定の情報をリクエストします。
ブラウザの振り分けに関して大丈夫だけど、他のブラウザがどうなるかは分からないし、個人情報を守るための信頼性が確保される新しい方のNavigator.userAgentDataプロパティへ移行する流れがあるので、サイト作成でも使わないと不十分な場合が出て来ないとはかぎらない。
Navigator.userAgentプロパティでのブラウザ名やOS名の取得について
以前はブラウザ名やOS名を、直接、取得する方法がなくて代替案としてNavigator.userAgentプロパティが用いられた。
訪問者のユーザーエージェントを取得することができてブラウザ名やOS名も含まれているからブラウザをデバイス毎に判別することに使えた。
信頼性が完全なものではないからブラウザを確実に判別したい場合は使わないように注意しなくてはならない。
メモ: 仕様書では、ブラウザーがこのフィールドを介して提供する情報をできるだけ少なくすることを求めています。このプロパティの値は、同じブラウザーの将来のバージョンでも同じままであると仮定してはいけません。まったく使用しないようにしたり、ブラウザーの現在のバージョンと過去のバージョンのためだけに使用するようにしてください。新しいブラウザーは、古いブラウザーと同じ UA、またはその一部を使い始めるかもしれません。ブラウザーエージェントが本当にこのプロパティによって広告されたものであるという保証は本当にありません。
また、ブラウザーのユーザーはこのフィールドの値を変更することができることを覚えておいてください (UA なりすまし)。
ユーザーエージェントの内容はいつも同じとはかぎらないことと訪問者が変更できることから信頼性がない。
サイトのデザインを、暫くの間、少しだけ止めるくらいならば使っても構わないのではないかと思う。
Navigator.userAgentプロパティの使い方
構文はwindow.navigator.useragent
、またはwindowを省略してnavigator.useragent
で、ユーザーエージェントを取得することができる。
現在の閲覧中のユーザーエージェントの内容
※Navigator.userAgentプロパティで取得して表示している。
ユーザーエージェントにブラウザやOSの名前が含まれているからそうした文字列があるかないかを調べるとブラウザをデバイス毎に判別できてコードの振り分けに使える。
if文でNavigator.userAgentプロパティで取得したユーザーエージェントをincludes()メソッドにかけてブラウザ名が含まれるかどうかを判別する。
ユーザーエージェントのブラウザ名は必ずしも一定ではないから文字列の英語の大文字と小文字をどちらかに統一した方が使い易いので、大文字に纏めるtoUpperCase()メソッドか小文字に纏めるtoLowerCase()メソッドもやっておく方が良いと思う。
ブラウザ名はNavigator.userAgentDataのbrandの「Microsoft Edge」の「Microsoft」や「Google Chrome」の「Google」のようなメーカー名も付くとはかぎらないので、ブラウザ名だけで、条件付ける。
加えて主要なブラウザではEdgeが「Edg」になったり、ブラウザ名を、そのまま、使っても判別できない場合には変更する必要がある。
どう振り分けるかはNavigator.userAgentDataプロパティの場合と同じで、条件がブラウザ名だけならばif文を、そのまま、使ってブラウ名とOS名などの複数ならぱ入れ子にして使う。
Chromeを判別するサンプルのソースコード
const uainfo = navigator.userAgent.toLowerCase();
if (uainfo.includes("chrome")) {
// Chromeで使用したいソースコード
} else {
// Chrome以外で使用したいソースコード
}
WindowsのChromeを判別するサンプルのソースコード
const uainfo = navigator.userAgent.toLowerCase();
if (uainfo.includes("chrome")) {
if (uainfo.includes("windows")) {
// WindowsのChromeで使用したいソースコード
} else {
// Windows以外のChromeで使用したいソースコード
}} else {
if (uainfo.includes("windows")) {
// WindowsのChrome以外で使用したいソースコード
} else {
// Windows以外のChrome以外で使用したいソースコード
}}
ユーザーエージェントは変更される可能性があるけど、現時点で異なるブラウザで同じ文字列が含まれる場合があり、EdgeのユーザーエージェントにChromeとSafari、ChromeのユーザーエージェントにSafarが含まれるし、異なるOSで同じ文字列が含まれる場合があり、AndroidのユーザーエージェントにLinuxが含まれる状況だから振り分けの仕方に注意を要する。
複数のブラウザ名やOS名の文字列を含むものを振り分けたいときは他のものの振り分けに含まれないようにする。
複数のブラウザ名を持つユーザーエージェントの振り分けの例
const uainfo = navigator.userAgent.toLowerCase();
if (uainfo.includes("edg")) {
// で使用したいソースコード
} else if (uainfo.includes("chrome")) {
// Chromeで使用したいソースコード
} else if (uainfo.includes("safari")) {
// Safariで使用したいソースコード
} else {
// EdgeとChromeとSafari以外で使用したいソースコード
}
複数のOS名を持つユーザーエージェントの振り分けの例
const uainfo = navigator.userAgent.toLowerCase();
if (uainfo.includes("android")) {
// Androidで使用したいソースコード
} else if (uainfo.includes("linux")) {
// Linuxで使用したいソースコード
}
if文で複数のブラウザやOS名の文字列を含むものを先に振り分けてからその他のものをelse ifや入れ子で後から振り分ければ混ざることはない。
コメント