ブラウザを判定する(ブラウザスニッフィング)

はじめに

ブラウザをユーザーエージェントの使用なしに、ブラウザの固有動作を元に判定します。

これは、ユーザーエージェント偽装などへの対策として優秀です。また、今後のブラウザの増加などで意図せずユーザーエージェント文字列の衝突による誤判定を防ぐこともできます。(ただし、動作の変更により問題が発生する可能性があります)

ブラウザを判定する

ua.jsvar ua = (function() {
  return {
    IE:     /*@cc_on!@*/false || !!document.documentMode, // IE 4-11
//  Trident:!!document.uniqueID,                          // IE (MSHTML), Edge Legacy (EdgeHTML)
    Gecko:  'MozAppearance' in document.documentElement.style,
                                                          // Firefox *
    WebKit: /constructor/i.test(window.HTMLElement) || (!window.chrome && !!window.webkitAudioContext),
                                                          // Safari, iOS Third Party Browser, Chrome 27-
    Blink:  !!window.chrome,                              // Chrome 1+, Edge 79+, Opera 14+
//  Presto: !!window.opera,                               // Opera 12-
    Mobile: 'orientation' in window,
    Touch:  'ontouchstart' in window
  };
})();

実行例

IE を判定する

// IE 4-10
var isIE = /*@cc_on!@*/false;

// IE 8-11 (EmulateIE 5, 7-11)
var isIE = !!document.documentMode;

// IE 4-11
var isIE = /*@cc_on!@*/false || !!document.documentMode;

@cc_on

@cc_on ~ @は、条件付きコンパイル機能を有効にする記述です。

条件付きコンパイルは、 JScript の機能でバージョン毎に互換性を維持しながら JScript の新機能を使用するための機能です。 IE 4 (JScript 3.0) で登場して、 IE 11 で廃止された。当然ながら、 IE 以外では実装されていません。

/*@cc_on!@*/は、!(否定)の処理を実行するという意味です。 そのため、/*@cc_on!@*/false!falseとなり。対応ブラウザではtrueとして処理されます。非対応ブラウザでは、/*@cc_on!@*/はコメント扱いであるため、falseとして処理されます。

※対応時期については、諸説あるようです(IE 10-, IE 4-10)

document.documentMode

document.documentModeは、 IE 8 で実装されたドキュメントモードの機能です。ドキュメントモードで指定したバージョンの IE でエミュレートしてウェブページを表示します。

IE 5, 7-11 をエミュレートできます。
ドキュメントモードの指定は、<meta http-equiv="X-UA-Compatible">を使用してください。

document.documentModeは、ブラウザのバージョンではありません。
 エミュレート中のドキュメントモードのバージョンです。
※ Edge Legacy は、document.documentModeに非対応です。
 <meta http-equiv="X-UA-Compatible">は、対応しています。
※ Windows 10 以降では、ドキュメントモードは非推奨です。
ドキュメント モードとエンタープライズ モード | Microsoft Learn
IE対策の「X-UA-Compatible:IE=edge」は必要か?

document.all

この方法は既に利用できません。 IE 以外のブラウザが document.all に対応したため、この方法は利用できません。

Trident を判定する

// IE, Edge Legacy
var isTrident = !!document.uniqueID;

// IE 4-11, Edge Legacy
var isTrident = /*@cc_on!@*/false || !!document.uniqueID;

※「Trident」は、 IE, Edge Legacy のレタリングエンジンの名称です。
※ Trident は、ライブラリファイルの名称から MSHTML とも呼ばれています。
※ Edge Legacy は、 Trident からフォークされた EdgeHTML を使用しています。

document.uniqueID の対応時期

正確な対応時期は不明だが、 IE 9 で実装済みであることは次ソースからうかがえる。実際はそれよりもまえに実装されているものと考えられます。

Gecko 判定について

// Firefox *
var isFF = 'MozAppearance' in document.documentElement.style;

// Firefox 1.5+
var isFF = 'InstallTrigger' in window;
var isFF = typeof InstallTrigger !== 'undefined';

※「Gecko」は、Firefox と派生ブラウザのレタリングエンジンの名称です。
window.InstallTriggerは、 Firefox の旧拡張機能をウェブページからインストールするための機能です。WebExtension(新拡張機能)には対応していません。現在は、初期設定でnull初期化されており、使用することはできません。いつなくなってもおかしくない機能です。
 1754441 - Deprecate InstallTrigger

window.globalStorage

// Firefox 2-13
var isFF = window.globalStorage;

この方法は既に利用できません。 Firefox 13 で window.globalStorage が削除されました。

WebKit を判定する

// Safari, Chrome 27-
var isSafari = /constructor/i.test(window.HTMLElement) || (!window.chrome && !!window.webkitAudioContext);

// Safari 9-, Chrome 27-
var isSafari = /constructor/i.test(window.HTMLElement);

// Safari 3.1+
var isSafari = !window.chrome && !!window.webkitAudioContext;

※ WebKit は、現在主に Safari / iOS Third Party Browser で使用されています。

'WebkitAppearance' in document.documentElement.style

var isWebKit = !window.chrome && 'WebkitAppearance' in document.documentElement.style;

この方法は既に利用できません。 Firefox 64+ が-webkit-appearanceに対応したことにより WebKit の判定として機能していません。

/constructor/i.test(window.HTMLElement)

// Safari 9-
var isSafari = /constructor/i.test(window.HTMLElement);
var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;

この方法は、既に利用できません。 Safari10 から機能していません。

Object.prototype.toString.call(window.HTMLElement)は、[object HTMLElementConstructor]を期待しています。

'webkitAudioContext' in window

// Safari 3.1+
var isSafari = !window.chrome && !!window.webkitAudioContext;

webkit プレフィックスを使用した単純な判定です。 AudioContext に特別な意味はありません。

そのため、「webkit プレフィックスが廃止される」又は「webkit プレフィックスに別ブラウザが対応した」場合修正が必要になります。

Blink を判定する

// Chrome 1+ (Headless Chrome を除く), Edge 79+ (Edge Chronium), Opera 14+
var isBlink = !!window.chrome;

// Chrome 1-70
var isChrome = !!window.chrome && (!!window.chrome.webstore || !!window.chrome.runtime);

Blink は、 WebKit のフォークです。そのため、古いバージョンの Chromium 系のブラウザは、 WebKit として判定されます。

※ Chrome 28 以降 Blink が使用されています。
※ Headless Chrome は、window.chromeが存在しないため、判定できません。

window.chrome

Chrome 1+ から存在する機能です。window.chromeは、インラインインストール(chrome.webstore.install()、Chrome Web Store を経由しなくても Chrome 拡張機能を追加できるインストールボタンをウェブサイトに配置できる機能)などを使用するための Chronium 固有の機能です。ただし、インラインインストールは Chrome 2 から非推奨となり、 Chrome 71 で廃止された。

ただし、 Chrome 1 から存在するため、正確には Blink を判定できていません。 Chrome 1-27 は、 WebKit であるため、正確な Blink 判定はではありません。ですが、困ることもないため、このままとします。

Presto を判定する

// Opera 12-
var isPresto = !!window.opera;

※「Presto」は、Opera 12 までのレタリングエンジンの名称です。
※ Opera は、Opera 14 から Chromium ベースで開発されているため、 Blink で判定します。

window.opera

window.opera は、 Chronium 対応により削除されました。

window.opera がいつから存在するのか確認できていません、ですが最低でも Opera 8 の時点では存在していたようです。

備考:スマホ・タブレット・デスクトップを判定する

var isMobile  = 'orientation' in window;
var isPhone   = Math.min(window.screen.width, window.screen.height) < 512;
var isTablet  =  isMobile && !isPhone;
var isDesktop = !isMobile && !isPhone;

※画面サイズ(ピクセルサイズ)境界の 512 に根拠はありません。
 最近の画面サイズの動向を元に適時変更してください。

備考:機能毎に判定する

特定の機能に対応しているか否かは、ブラウザ単位で判定すべきではありません。機能単位で実装有無を確認して個別に対応すべきです。

if (window.requestAnimationFrame) {
  window.requestAnimationFrame(function() {
    // ...
  });
}
@supports (display: grid) {
  div {
    display: grid;
  }
}

@supportsは、 Firefox22+, Safari9+, Chrome28+ で利用可能です。
 @supports - CSS: カスケーディングスタイルシート | MDN
※ CSS によるブラウザ判定は、本記事の主題ではないため、簡略します。

参考