关闭

JavaScript学习--Item36 客户端检测技术

3672人阅读 评论(0) 收藏 举报
分类:

JavaScript 客户端检测

JavaScript 客户端检测是指通过javascript,判断客户端的环境,诸如使用的浏览器和操作系统。客户端检测是一种行之有效的开发策略。但不到万不得已,就不要使用客户端检测。先设计通用的方案,然后根据浏览器之间的差异和各自的怪癖quirky,再使用特定于浏览器的技术增强该方案。

user-agent字符串检测法是最原始的浏览器检测方式,每个访问网站的程序都必须提供一个user-agent字符来向服务器确定他的身份。navigator对象包含一些浏览器信息,诸如名称、版本号和平台。

//取得用户代理字符串 并全部小写。
var ua = navigator.userAgent.toLowerCase();
document.write(ua);
if(us.indexOf('msie')>0){
    alert('IE');
}else{
    alert('W3C');
}

一般涉及到的检测类型包括下面这几种:

1、识别呈现引擎
引擎主要包含四种:IE、Gecko、WebKit、Opera

2、识别浏览器
主流浏览器包含四种:IE、Chrome、Firefox、Opera

3、识别平台
主流平台包含三类:Windows、Mac、Unix

4、识别Windows操作系统
Windows操作系统包含:Windows 98、Window NT、Window XP、Window Vista、Windows 7…

5、识别移动设备
主流的移动设备包含三类:iPhone、iPod、Anroid、Nokia

1 能力检测

能力检测的目标不是识别特定的浏览器,而是识别浏览器的能力,知道浏览器能干什么不能干什么。能力检测的基本模式如下:

if(object.propertyInQuestion){
   //使用object.propertyInQuestion
}

//判断document是否支持getelementById方法
if(document.getElementById){
   return document.getElementById();
}

两个重要的概念:

  • 第一个概念是先检测达成目的的最常用的特性。先检测最常用的特性,可以保证代码最优化,因为在多数情况下都可以避免测试多个条件。
  • 第二个概念是必须测试实际要用到的特性。一个特性存在,不一定意味着另一个特性也存在。

可靠的能力检测

如果判断某个对象是否支持排序方法,可以使用操作符typeof

   //只检测了是否存在相应的方法
function isSortable(object){
       return !!object.sort;  
}
var result= isSortable({sort:true});

在可能的情况下,尽量使用typeof进行能力检测。

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

但是并不是所有浏览器都会返回的值都合理。大部分浏览器在检测到document.createElement存在时,都会返回ture,但是在IE8及之前的IE版本会返回false。

function hasCreateElement(){
       return  typeof document.createElement =="function ";  
}

IE8之前的版本返回false.因为DOM对象是宿主对象,是通过COM而不是JScript实现的,document.createElement()函数是一个COM对象。IE9已更正。

 var xhr=new ActiveXObject("Microsoft.XMLHttp");
 if(xhr.open){ //error
     //TO-DO
 }

直接把函数属性访问会导致JS错误。typeof xhr.open 返回“unknown”

在浏览器环境测试任何对象的某个特性是否存在使用如下函数:

   //Peter Michaux
   function isHostMethod(object, property) {
       var t = typeof object[property];
       return t == 'function' ||
           (!!(t == 'object' && object[property])) ||
               t == 'unknown';
   }

   result = isHostMethod(xhr, "open"); //true
   result = isHostMethod(xhr, "foo"); //false

检测某个或某几个特性并不能够确定浏览器。实际上,根据浏览器不同将能力组合起来是更可取的方式。如果知道自己的应用程序需要使用某些特定的浏览器特性,那么最好是一次性检测所有相关特性,而不要分别检测。

//确定浏览器是否支持 Netscape 风格的插件
var hasNSPlugins = !!(navigator.plugins && navigator.plugins.length );

//确定浏览器是否具有 DOM1 级规定的能力
var hasDOM1 = !!(document.getElementById && document.createElement 
                && document.getElementByTagName);

2. 怪癖检测 Quirkys Detection

目标识别浏览器特殊的行为,与能力检测不同,是想知道浏览器存在的什么缺陷.运行一小段代码,以确定某一特性不能正常工作.

“怪癖”都是个别浏览器所独有的,而且通常被归为 bug。由于检测“怪癖”涉及运行代码,因此建议仅检测那些对你有直接影响的“怪癖”,而且最好在脚本一开始就执行此类检测,以便尽早解决问题。

例如,IE中存在的一个 bug ,即如果某个实例属性与标记为 [[DontEnum]] 的某个原型属性同名,那么该实例属性将不会出现在 fon-in 循环当中。可以使用如下代码来检测这种“怪癖”:

var hasDontEnumQuirk = function(){
    var o = { toString : function(){}};

    for( var prop in o){
        if( prop == "toString"){
            return false;
        }
    }
    return true;
}();

3. 用户代理检测

  争议最大的一种客户端检测技术。用户代理检测通过检测用户代理字符串来确定实际使用的浏览器。在每一次HTTP请求过程中,用户代理字符串是作为响应首部发送的,而该字符串可以通过 JavaScript 的 navigator.userAgent 属性访问。在服务器端,通过检测用户代理字符串来确定用户使用的浏览器是一种常用而且广为接受的做法。而在客户端,用户代理检测一般被当作一种万不得已才使用的做法,其优先级排在能力检测和怪癖检测之后。 

3.1 识别呈现引擎

  确切的知道浏览器的名字和版本号不如确定他们使用的是什么呈现引擎。如果Firefox、Camino 和 Netsacpe 都使用相同版本的 Gecko ,那么他们一定支持相同的特性。类似的,不管是什么浏览器,只要它跟 Safari 3 使用的是同一个版本的 WebKit,那么该浏览器也就跟 Safari 3 具备同样的功能。因此,我们需要编写的脚本将主要检测五大呈现引擎: IE、Gecko、WebKit、KHTML 和 Opera1。

  注1:现在Opera 和 Chrome将采用基于WebKit开发自主渲染引擎Blink。

  模块增强模式封装检测脚本。

var client = function(){

    //呈现引擎
    var engine = {
        ie : 0,
        gecko : 0,
        webkit : 0,
        khtml : 0,
        opera : 0,

        //具体的版本号
        ver : null
    };

    //再次检测呈现引擎、平台和设备
    return {
        engine : engine
    };
}();

  如果检测到那个呈现引擎,就以浮点数值形式将该引擎的版本号写入相应的属性。而呈现引擎的完整版本是一个字符串,则被写入ver属性。

if(client.engine.ie){   //如果是IE ,engine.ie的值应大于0
    //针对IE
} else if (client.engine.gecko > 1.5) {
    if(client.engine.ver == "1.8.1"){
        //to-do
    }

}

  检测到一个呈现引擎后,其 client.engine 中对应的属性将被设置成一个大于 0 的值,该值可以转换成布尔值的 true。这样就可以在 if 语句中检测相应的属性,以确定当前使用的呈现引擎,连具体的版本号都不需要考虑。鉴于每个属性都包含一个浮点数值,因此有可能丢失某些版本信息。例如,将字符串”1.8.1”传入 parseFloat() 后悔得到数值 1.8。不过,在必要的时候,可以检测 ver 属性,该属性中保存着完整的版本信息。

  正确的识别呈现引擎关键是检测顺序要正确。

  首先应该检测的是 Opera,因为它的用户代理字符串可能完全模仿其他浏览器。

//Opera 5起支持window.opera对象
if ( window.opera ){
    engine.ver = window.opera.version();  //Opera 7.6起返回浏览器版本字符串
    engine.opera = parseFloat( engine.ver );
}

  其次检测 WebKit.因为WebKit的用户代理字符串包含“Gecko”和“KHTML”,若先检测他们,可能会出现错误的结论。

//AppleWebKit独一无二
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);
}

  仅供参考,不保证精确。

  接下来测试 KHTML。KHTML的用户代理字符串包含“Gecko”,所以在排除KHTML之前是无法正确检测基于Gecko的浏览器。Konqueror 3.1及更早的版本不包含KHTML的版本,要使用Konqueror的版本来替代。

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) || /Konqueror\/([^;]+)/.test(ua)){
    engine.ver = RegExp["$1"];
    engine.khtml = parseFloat(engine.ver);
}

  下面我们就可以准确的检测Gecko。Gecko的版本号位于字符串“rv:”与一个闭括号之间,还要查找“Gecko/”后的8个数字。

//Windows XP 下的 Firefox 2.0.0.11 :
Mbzilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.

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) || /Konqueror\/([^;]+)/.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);
} 

最后是IE.IE版本号位于MSIE的后面。

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) || /Konqueror\/([^;]+)/.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 = browser.ver = RegExp["$1"];
    engine.ie = browser.ie = parseFloat(engine.ver);
}

3.2.识别浏览器

  识别了浏览器的呈现引擎就足以为我们采取正确的操作提供依据了。可是,只有呈现引擎还不能够说明存在所需的JavaScript功能。相同内核的浏览器的JavaScript有可能引擎不一样:safari 和chrome。

var client = function(){
    //呈现引擎

    var engine = {
        ie : 0,
        gecko : 0,
        webkit : 0,
        khtml : 0,
        opera : 0,

        //具体的版本号
        ver : null
    };

    var browser = {
        //浏览器
        ie : 0,
        firefox : 0,
        konq : 0,
        opera : 0,
        chrome : 0,
        safari : 0,

        //具体的版本
        ver : null
    };

    //再次检测呈现引擎、平台和设备

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

  由于大多数浏览器与其呈现引擎密切相关,所以下面示例中检测浏览器的代码与呈现引擎的代码是混合在一起的

//检测呈现引擎及浏览器
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);

    //确定是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"];
        borwser.safari = parseFloat(browser.ver);
    } else {
        //近似的确定版本号
        var safariVersion = 1;

        if (engine.webkit < 100 ){
            safariVersion = 1;
        } else if (engine.webkit < 312){
            safariVersoin = 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 = RegExp["$1"];
    engine.khtml = 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);
}

我们就可以这样判断了:

if(client.engine.webkit){   
    if (client.browser.chrome) {

    }else if (client.browser.safari){

    }
} else if (client.engine.gecko) {
    if (client.browser.firefox) {

    }else{
    
    }
}

3.3.识别平台

  很多时候,只要知道呈现引擎就可以编写出适合的代码了,但在有些情况下,平台可能是必须关注的问题。三大主流平台:Windows、Mac、Unix(Linux)为了检测这些平台,需要再添加一个对象:

var client = function(){
     //呈现引擎

     var engine = {
         ie : 0,
         gecko : 0,
         webkit : 0,
         khtml : 0,
         opera : 0,

         //具体的版本号
         ver : null
     };

     var browser = {
         //浏览器
         ie : 0,
         firefox : 0,
         konq : 0,
         opera : 0,
         chrome : 0,
         safari : 0,

         //具体的版本
         ver : null
     };

     var system = {
         win : false,
         mac : false,
         xll : false
     };

     //再次检测呈现引擎、平台和设备

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

 //navigator.platform值Win32 、Win64、MacPPC、MacIntel、X11、Linux i686
 var p = navigator.platform;
 system.win = p.indexOf("Win") == 0;
 systemp.mac = p.indexOf("Mac") == 0;
 system.xll = (p.indexOf("Xll")) == 1 || (p.indexOf("Linux") == 0);

参考:JavaScript客户端检测技术

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:311426次
    • 积分:5515
    • 等级:
    • 排名:第5340名
    • 原创:271篇
    • 转载:19篇
    • 译文:0篇
    • 评论:17条
    文章分类
    最新评论