客户端检测之用户代理检测 — navigator.userAgent?

哈希冰川
• 阅读 2886

前言

前端这东西,各种先有事实后有标准。不管是各大浏览器老哥各自为政,还是w3c姗姗来迟,既有事实标准难以更改。虽说多方割据,互相竞争,总比一家独大,爱更不更来得好。但却苦了我们这些平头小码农?,颈椎病又加深了啊
之前阅读了《JavaScript高级程序设计》,今日准备将其用户代理检测源码部分做个归纳。
槽不多吐,开始正文!
博文地址:客户端检测之用户代理检测 — navigator.userAgent

1.客户端检测

面对各浏览器普遍存在的不一致性问题,开发人员就得利用各种客户端检测方法,来突破或规避各种局限性。
检测Web客户端的手段很多,各有利弊。在服务器端,用户代理检测是一种常用且广为接收的做法。通过对浏览器发送的用户代理字符串的内容进行检测,来识别用户的浏览器。

2.navigator.userAgent

BOM,即浏览器对象模型。BOM提供了很多对象,用于访问浏览器的功能,且这些功能与网内内容无关。其主要方面已被w3c加入HTML5豪华套餐。
BOM包含windowlocationnavigatorscreenhistory对象,navigator对象中的userAgent属性便是用户代理字符串

3.用户代理检测代码

var ua = navigator.userAgent;//用户代理字符串

3.1 呈现引擎检测

主要检测五大呈现引擎(渲染引擎):IE、Gecko、WebKit、KHTML 和 Opera。
为了不在全局作用域中添加多余的变量,使用模块增强模式来封装检测脚本。检测脚本的基本代码结构如下所示:

var client = function () {
    var engine = {
        //呈现引擎
        ie: 0,
        gecko: 0,
        webkit: 0,
        khtml: 0,
        opera: 0,
        //具体的版本号
        ver: null
    };
    //检测呈现引擎

    return {
        engine: engine
    };
}();

client全局变量,用于保存相关信息。匿名函数内部定义了一个局部变量engine,每个呈现引擎都对应着一个属性,属性的值默认为0。如果检测到了哪个呈现引擎,那么就以浮点数值形式将该引擎的版本号写入相应的属性。而呈现引擎的完整版本(是一个字符串),则写入ver属性。

//检测呈现引擎
var ua = navigator.userAgent;
if (window.opera) {
        engine.ver = window.opera.version();
        engine.opera = parseFloat(engine.ver);
    } else if (/AppleWebKit\/(\S+)/.test(ua)) {
        engine.ver = RegExp["$1"];
        engine.webkit = parseFloat(engine.ver);
    } else if (/KHTML\/(\S+)/.test(ua)) {
        engine.ver = RegExp["$1"];
        engine.khtml = parseFloat(engine.ver);
    } else if (/rv:([^\)]+)\) Gecko\/\d{8}/.test(ua)) {
        engine.ver = RegExp["$1"];
        engine.gecko = parseFloat(engine.ver);
    } else if (/MSIE ([^;]+)/.test(ua)) {
        engine.ver = RegExp["$1"];
        engine.ie = parseFloat(engine.ver);
    }

3.2 识别浏览器

var client = function(){
    var engine = {
        //呈现引擎
        ie: 0,
        gecko: 0,
        webkit: 0,
        khtml: 0,
        opera: 0,
        //具体的版本
        ver: null
    };
    var browser = {
        // 浏览器
        ie: 0,
        firefox: 0,
        safari: 0,
        konq: 0,
        opera: 0,
        chrome: 0,
        // 具体的版本
        ver: null
    };
    //检测呈现引擎和浏览器

    return {
        engine: engine,
        browser: browser
    };
}();

代码中又添加了私有变量browser ,用于保存每个主要浏览器的属性。与engine变量一样,除了当前使用的浏览器,其他属性的值将保持为0;如果是当前使用的浏览器,则这个属性中保存的是浮点数值形式的版本号。同样,ver属性中在必要时将会包含字符串形式的浏览器完整版本号。
由于大多数浏览器与其呈现引擎密切相关,所以下面示例中检测浏览器的代码与检测呈现引擎的代码是混合在一起的。

//检测呈现引擎及浏览器
var ua = navigator.userAgent;
if (window.opera) {
    engine.ver = browser.ver = window.opera.version();
    engine.opera = browser.opera = parseFloat(engine.ver);
} else if (/AppleWebKit\/(\S+)/.test(ua)) {
    engine.ver = RegExp["$1"];
    engine.webkit = parseFloat(engine.ver);
    // 确定是 Chrome  还是 Safari
    if (/Chrome\/(\S+)/.test(ua)) {
        browser.ver = RegExp["$1"];
        browser.chrome = parseFloat(browser.ver);
    } else if (/Version\/(\S+)/.test(ua)) {
        browser.ver = RegExp["$1"];
        browser.safari = parseFloat(browser.ver);
    } else {
        // 近似地确定版本号
        var safariVersion = 1;
        if (engine.webkit < 100) {
            safariVersion = 1;
        } else if (engine.webkit < 312) {
            safariVersion = 1.2;
        } else if (engine.webkit < 412) {
            safariVersion = 1.3;
        } else {
            safariVersion = 2;
        }
        browser.safari = browser.ver = safariVersion;
    }
} else if (/KHTML\/(\S+)/.test(ua) || /Konqueror\/([^;]+)/.test(ua)) {
    engine.ver = browser.ver = RegExp["$1"];
    engine.khtml = browser.konq = parseFloat(engine.ver);
} else if (/rv:([^\)]+)\) Gecko\/\d{8}/.test(ua)) {
    engine.ver = RegExp["$1"];
    engine.gecko = parseFloat(engine.ver);
    // 确定是不是 Firefox
    if (/Firefox\/(\S+)/.test(ua)) {
        browser.ver = RegExp["$1"];
        browser.firefox = parseFloat(browser.ver);
    }
} else if (/MSIE ([^;]+)/.test(ua)) {
    engine.ver = browser.ver = RegExp["$1"];
    engine.ie = browser.ie = parseFloat(engine.ver);
}

3.3 识别系统平台

很多时候,只要知道呈现引擎就足以编写出适当的代码了。但在某些条件下,平台可能是必须关注的问题。那些具有各种平台版本的浏览器(如 Safari、Firefox 和 Opera),在不同的平台下可能会有不同的问题。目前的三大主流平台是 Windows、Mac 和 Unix(包括各种 Linux)。
再添加一个新对象:

var client = function () {
    var system = {
        win: false,
        mac: false,
        x11: false
    };
    //检测设备
    var p = navigator.platform;
    system.win = p.indexOf("Win") == 0;
    system.mac = p.indexOf("Mac") == 0;
    system.x11 = (p.indexOf("X11") == 0) || (p.indexOf("Linux" == 0);
    return {
        engine: engine,
        browser: browser,
        system: system
    };
}();

3.3.1 识别具体Windows系统

Windows平台下,还可以从用户代理字符串中进一步取得具体的操作系统信息。下表列出了不同浏览器在表示不同的Windows操作系统时给出的不同字符串:
客户端检测之用户代理检测 — navigator.userAgent?
由于用户代理字符串中的Windows操作系统版本表示方法各异,因此检测代码并不十分直观。好在,从 Windows 2000 开始,表示操作系统的字符串大部分都还相同,只有版本号有变化。为了检测不同的Windows操作系统,必须要使用正则表达式。由于使用 Opera 7 之前版本的用户已经不多了,因此我们可以忽略这部分浏览器。

if (system.win) {
    if (/Win(?:dows )?([^do]{2})\s?(\d+\.\d+)?/.test(ua)) {
        if (RegExp["$1"] == "NT") {
            switch (RegExp["$2"]) {
                case "5.0":
                    system.win = "2000";
                    break;
                case "5.1":
                    system.win = "XP";
                    break;
                case "6.0":
                    system.win = "Vista";
                    break;
                case "6.1":
                    system.win = "7";
                    break;
                default:
                    system.win = "NT";
                    break;
            }
        } else if (RegExp["$1"] == "9x") {
            system.win = "ME";
        } else {
            system.win = RegExp["$1"];
        }
    }
}

3.3.2 识别移动设备

var client = function () {
    var system = {
        win: false,
        mac: false,
        x11: false,
        
        // 移动设备
        iphone: false,
        ipod: false,
        ipad: false,
        ios: false,
        android: false,
        nokiaN: false,
        winMobile: false
    };

    //检测移动设备
    system.iphone = ua.indexOf("iPhone") > -1;
    system.ipod = ua.indexOf("iPod") > -1;
    system.ipad = ua.indexOf("iPad") > -1;
    system.nokiaN = ua.indexOf("NokiaN") > -1;
    //windows mobile
    if (system.win == "CE") {
        system.winMobile = system.win;
    } else if (system.win == "Ph") {
        if (/Windows Phone OS (\d+.\d+)/.test(ua)) {
            system.win = "Phone";
            system.winMobile = parseFloat(RegExp["$1"]);
        }
    }
    //检测 iOS 版本
    if (system.mac && ua.indexOf("Mobile") > -1) {
        if (/CPU (?:iPhone )?OS (\d+_\d+)/.test(ua)) {
            system.ios = parseFloat(RegExp.$1.replace("_", "."));
        } else {
            system.ios = 2; //不能真正检测出来,所以只能猜测
        }
    }
    //检测 Android 版本
    if (/Android (\d+\.\d+)/.test(ua)) {
        system.android = parseFloat(RegExp.$1);
    }

    return {
        engine: engine,
        browser: browser,
        system: system
    };
}();

3.3.3 识别游戏系统

除了移动设备之外,视频游戏系统中的 Web 浏览器也开始日益普及。任天堂 WiiPlaystation 3 或者内置 Web 浏览器,或者提供了浏览器下载。Wii 中的浏览器实际上是定制版的 Opera,是专门为 Wii Remote 设计的。Playstation 的浏览器是自己开发的,没有基于前面提到的任何呈现引擎。这两个浏览器中的用户代理字符串如下所示:

Opera/9.10 (Nintendo Wii;U; ; 1621; en)
Mozilla/5.0 (PLAYSTATION 3; 2.00)

第一个字符串来自运行在 Wii 中的 Opera,它忠实地继承了 Opera 最初的用户代理字符串格式。第二个字符串来自 Playstation 3,虽然它为了兼容性而将自己标识为 Mozilla 5.0,但并没有给出太多信息,而且设备名称全部使用了大写字母。

var client = function () {
    var system = {
        win: false,
        mac: false,
        x11: false,
        
        // 移动设备
        iphone: false,
        ipod: false,
        ipad: false,
        ios: false,
        android: false,
        nokiaN: false,
        winMobile: false,

        // 游戏系统
        wii: false,
        ps: false
    };

    //检测游戏系统
    system.wii = ua.indexOf("Wii") > -1;
    system.ps = /playstation/i.test(ua);

    return {
        engine: engine,
        browser: browser,
        system: system
    };
}();

3.4 完整代码

UserAgentDetection.js

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Karen110 Karen110
3年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
哈希冰川
哈希冰川
Lv1
春分至情花开,不负春光不负你。
文章
7
粉丝
0
获赞
0