客户端检测

能力检测

又称特性检测,测试浏览器是否支持某种特性。

如,在IE5之前版本中没有document.getElementById()这个DOM方法,但可以通过document.all属性实现同样的功能。

function getElement(id){
    if(document.getElementById){
        return document.getElementById(id);
    }else if(document.all){
        return document.all[id];
    }else{
        throw new Error("No Way to retrieve element")
    }
}

安全能力检测

能力检测最有效的场景是检测能力是否存在的同时,验证其是否能够展现出预期的行为。

//错误的能力检测,只能检测到能力是否存在
function isSortable(object){
    return !!object.sort;
}

这个函数尝试通过检测对象上是否有sort()方法来确定它是否支持排序。问题在于,即使这个对象有一个sort属性,这个函数也会返回true

简单地测试到一个属性存在并不代表这个对象就可以排序。更好的方式是检测sort是不是函数。

function isSortable(object){
    return typeof object.sort == "function"
}

基于能力检测进行浏览器分析

检测特性

按照能力将浏览器归类:

//检测浏览器是否支持Netscape式的插件
let hasNSPlugins =!!(navigator.plugins &&navigator.plugins.length);
//检测浏览器是否具有DOM level 1 能力
let hasDOM1 = !!(document.getElementById && document.createElement && document.getElementsByTagName);
检测浏览器

可以根据对浏览器特性的检测并与已知特性对比,确认用户使用的是什么浏览器。

//检测浏览器
  class BrowserDetector{
    constructor() {
      //测试条件编译
      //IE6~10支持
      this.isIE_Gte6Lte10 = /*@cc_on!@*/false;

      //测试documentMode
      //IE7~11支持
      this.isIE_Gte7Lte11 = !!document.documentMode;

      //测试StyleMedia构造函数
      //Edge 20及以上版本支持
      this.isEdge_Gte20= !!window.StyleMedia;

      //测试Firefox专有扩展按照API
      //所有版本的Firefox都支持
      this.isFirefox_Gte1=typeof InstallTrigger !== 'undefined';

      //测试chrome对象及其webstore属性
      //Opera的某些版本有window.chrome,但没有window.chrome.webstore
      //所有版本的chrome都支持
      this.isChrome_Gte1 =!! window.chrome &&!!window.chrome.webstore;

      //Safari早期版本会给构造函数的标签符追加"Constructor"字样
      //window.Element.toString();//[object ElementConstructor]
      //Safari 3~9.1支持
      this.isSafari_Gte3Lte9_1=/constructor/i.test(window.Element);

      //推送通知API暴露在window对象上
      //使用默认参数值以避免对undefined调用toString()
      //Safari 7.1及以上版本支持
      this.isSafari_Gte7_1=(({pushNotification={}}={})=>
        pushNotification.toString() == '[object SafariRemoteNotification]'
      )(window.safari);
      
      //测试addons属性
      //Opera 20及以上版本支持
      this.isOpera_Gte20 =!!window.opr &&!!window.opr.addons;
    }
    isIE(){return this.isIE_Gte6Lte10 || this.isIE_Gte7Lte11}
    isEdge(){return this.isEdge_Gte20 && !this.isIE();}
    isFirefox(){return this.isFirefox_Gte1;}
    isChrome(){return this.isChrome_Gte1;}
    isSafari(){return this.isSafari_Gte7_1 || this.isSafari_Gte3Lte9_1;}
    isOpera(){return this.isOpera_Gte20;}
  }

用户代理检测

通过浏览器的用户代理字符串确定使用的是什么浏览器。用户代理字符串包含在每个Http请求的头部,在JS中可以通过navigator.userAgent访问。

在服务器端,常见的做法是根据接收到的用户代理字符串确定浏览器并执行相应操作。

而在客户端,用户代理检测被认为是不可靠的。

维护比较频繁的第三方用户代理解析程序:

  • Bowser
  • UAParser,js
  • Platform.js
  • CURRENRT-DEVICE
  • Google Closure
  • Mootools

软件与硬件检测

识别浏览器与操作系统

navigatorscreen对象也提供了关于页面所在软件环境的信息。

navigator,oscpu

此属性是一个字符串,通常对应用户代理字符串中操作系统/系统架构相关信息。

比如,windows 10上的Firefox的oscpu属性应该对应与下面加粗的部分

console.log(navigator.userAgent)
"Mozilla/5.0 (windows NT 10.0;Win64; x64;rv:58.0) Gecko/20100101 Firefox/58/0"
console.log(navigator.oscpu)
"windows NT 10.0; Win64; x64;"

navigator.vendor

属性是一个字符串,通常包含浏览器开发商信息。返回这个字符串是浏览器navigator兼容模式的一个功能。

如,chrome中的这个navigator.vendor属性返回下面的字符串:

console.log(navigator.vendor)// "Google Inc"

navigator.platform

属性是一个字符串,通常表示浏览器所在的操作系统。

screen.colorDepthscreen.pixelDepth

返回一样的值,即显示器每像素颜色的位深。

screen.orientation

属性返回一个ScreenOrientation对象,其中包含Screen Orientation API定义的屏幕信息。这里面最有意思的属性是angle和type。前者返回相对于默认状态下屏幕的角度,后者返回一下4中枚举值之一:

  • Portrait-primary
  • Portrait-secondary
  • Landscape-primary
  • Landscape-secondary

浏览器元数据

Geolocation API

navigator.geolocation属性暴露了Geolocation API,可以让浏览器脚本感知当前设备的地理位置。这个API只在安全执行环境(通过HTTPS获取的脚本)中可用。

这个API可以查询宿主系统并金肯呢个精确地返回设备的位置信息。

获取浏览器当前的位置,可以使用getCurrentPosition()方法。这个方法返回一个Coordinates对象。

navigator.geolocation.getCurrentPosition((position)=> p =position)

这个position对象中有一个表示查询时间的时间戳,以及包含坐标信息,以及包含坐标信息的Coordinates对象。

Coordinates对象中包含标准格式的经度和纬度,以及以米为单位的精度。

Coordinates对象包含一个speed属性,表示设备每秒移动的速度。还有一个heading(朝向)属性,表示相对于正北方向移动的角度(0<heading<360)。