第4章 浏览器嗅探与特征侦测

虽然浏览器嗅探现在已经不推荐了,但在某些场合我们还是需要的,比如一些统计脚本。而特征侦测是Prototype时期发展起来的一个技术,具体是判定某个原生对象有没有此方法或属性,有时严格一些,则会执行这个方法,看它有没有达到预期的值。在标准浏览器里,它们提供了document.implementation.hasfeature方法,可惜有Bug,不准确。在本书快成形时,W3C又推出了CSS.supports方法,显然大家都对这块非常重视。

4.1 判定浏览器

主流浏览器有五个:IE、Firefox、Opera、Chrome、Safari。早期所有框架都是通过navigator.userAgent 进行判定,与浏览器商斗智斗谋。显然外国的浏览器商还是有良心的,换是国内的,基本判定不出。

jQuery给出的解决方案,现在已移出jQuery本体,成为一个插件。

      (function(jQuery, window, undefined) {
          "use strict";
          var matched, browser;
          jQuery.uaMatch = function(ua) {
              ua = ua.toLowerCase();
              var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
                      /(webkit)[ \/]([\w.]+)/.exec(ua) ||
                      /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
                      /(msie) ([\w.]+)/.exec(ua) ||
                      ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
                      [];
              var platform_match = /(ipad)/.exec(ua) ||
                      /(iphone)/.exec(ua) ||
                      /(android)/.exec(ua) ||
                      [];
              return {
                  browser: match[ 1 ] || "",
                  version: match[ 2 ] || "0",
                  platform: platform_match[0] || ""
              };
          };
          matched = jQuery.uaMatch(window.navigator.userAgent);
          browser = {};
          if (matched.browser) {
              browser[ matched.browser ] = true;
              browser.version = matched.version;
          }
          if (matched.platform) {
              browser[ matched.platform ] = true
          }
      // Chrome is Webkit, but Webkit is also Safari.
          if (browser.chrome) {
              browser.webkit = true;
          } else if (browser.webkit) {
              browser.safari = true;
          }
          jQuery.browser = browser;
      })(jQuery, window);

mass Framework给出的解决方案如下。

      //https://github.com/RubyLouvre/mass-Framework/blob/1.4/more/brower.js
      define("brower", function( ){
          var w = window,ver = w.opera ? (opera.version().replace(/\d$/, "") - 0)
          : parseFloat((/(?:IE |fox\/|ome\/|ion\/)(\d+\.\d)/.
              exec(navigator.userAgent) || [,0])[1]);
          return {
              //测试是否为IE或内核为trident,是则取得其版本号
              ie: !!w.VBArray && Math.max(document.documentMode||0, ver),//内核trident
              //测试是否为Firefox,是则取得其版本号
              firefox: !!w.netscape && ver,//内核Gecko
              //测试是否为Opera,是则取得其版本号
              opera:  !!w.opera && ver,//内核 Presto 9.5为Kestrel 10为Carakan
              //测试是否为Chrome,是则取得其版本号
              chrome: !! w.chrome &&  ver ,//内核V8
              //测试是否为Safari,是则取得其版本号
              safari: /apple/i.test(navigator.vendor) && ver// 内核 WebCore
          }
      });

mass成形较晚,是使用特征侦测实现的。

特征侦测的好处是浏览器不会随意去掉某一个功能,不过要注意不要使用标准属性与方法做判定依据。每个浏览器都有自己的私有实现,我们用它们做判定好了。下面是我收集的其他一些判定方法。

      ie = !!document.recalc
      ie = !!window.VBArray
      ie = !!window.ActiveXObject
      ie = !!window.createPopup;
      ie = /*@cc_on!@*/!1;
      ie = document.expando;//document.all在opera firefox的古老版本也存在
      ie = (function() {//IE10中失效
          var v = 3, div = document.createElement('div');
          while (div.innerHTML = '<!--[if gt IE ' + (++v) + ']><br><![endif]-->', div.innerHTML )
              ;
          return v > 4 ? v : !v;
      }());
      ie678 = !+"\v1";
      ie678 = !-[1, ];
      ie678 = '\v' == 'v';
      ie678 = ('a~b'.split(/(~)/))[1] == "b"
      ie678 = 0.9.toFixed(0) == "0"
      ie678 = /\w/.test('\u0130') //由群里的abcd友情提供
      ie8 = window.toStaticHTML
      ie9 = window.msPerformance
      ie678 = 0//@cc_on+1;
      ie67 = !"1"[0] //利用IE6或IE5的字符串不能使用数组下标的特征
      ie8 = !!window.XDomainRequest;
      ie9 = document.documentMode && document.documentMode === 9;
      //基于条件编译的嗅探脚本,IE会返回其JS引擎的版本号,非IE返回0
      var ieVersion = eval("''+/*@cc_on" + " @_jscript_version@*/-0") * 1
      ie9 = ieVersion === 5.9
      ie8 = ieVersion === 5.8
      ie7 = ieVersion === 5.7
      ie6 = ieVersion === 5.6
      ie5 = ieVersion === 5.5
      ie10 = window.navigator.msPointerEnabled
      ie11 = '-ms-scroll-limit' in document.documentElement.style
      opera = !!window.opera;
      //https://developer.mozilla.org/En/Windows_Media_in_Netscape
      firefox = !!window.GeckoActiveXObject
      firefox = !!window.netscape //包括firefox
      firefox = !!window.Components
      firefox = !!window.updateCommands
      safari = !!(navigator.vendor && navigator.vendor.match(/Apple/))
      safari = window.openDatabase && !window.chrome;
      chrome = !!(window.chrome && window.google)
        移动设备的相关判定。这个建议看jquery mobile与zepto的源代码。
      isIPhone = /iPhone/i.test(navigator.userAgent);
      isIPhone4 = window.devicePixelRatio >= 2//在网页中,pixel与point比值称为device-pixel-ratio,
      普通设备都是1,iPhone 4是2,有些Android机型是1.5
      //http://blog.webcreativepark.net/2011/01/25-173502.html
      isIPad = /iPad/i.test(navigator.userAgent);
      isAndroid = /android/i.test(navigator.userAgent);
      isIOS = isIPhone  || isIPad ;

国内的浏览器判定可以看Tangram或qwrap。它们基本是IE,webkit内核或blink内核。