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

能力检测 Feature Detection

能力检测(又称特性检测)目标是识别浏览器的能力而不是识别特定的浏览器。只需根据浏览器支持的功能给出解决方案。

能力检测作为确定下一步解决方案的依据,而不是用它来判断用户使用的是什么浏览器。

基本模式:

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

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

更可靠的能力检测

确定一个对象是否支持排序,检测对象是否支持sort()方法。

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

var result= isSortable({sort:true});

检测某个属性是否存在并不能确定对象是否支持排序。,检测sort是不是一个函数。

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

尽量使用typeof进行能力检测

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);

延伸:

http://peter.michaux.ca/

http://peter.michaux.ca/articles/feature-detection-state-of-the-art-browser-scripting

怪癖检测  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;
}();

用户代理检测

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

  电子欺骗(spoofing)是指 浏览器通过在自己的用户带来字符串加入一些错误或误导性信息,达到欺骗服务器的目的。

  阅读下面内容最好事先了解下用户代理字符串的知识,请阅读博文《用户代理字符串简史》。 

用户代理字符串检测技术

  当你了解用户代理字符串发展和使用方式后,使用客户端检测技术检测出特定的浏览器不是一个轻松的事情。一般知道呈现引擎和最低限度的版本就足以决定正确的操作方法。

1.识别呈现引擎

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

  注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);
}

  仅供参考,不保证精确。

Safari   最低限度的WebKit版本号 Safari   最低限度的WebKit版本号
 1.0-1.0.2  85.7 1.3.2   312.8
 1.03 85.8.2  2.0   412
1.1-1.1.1  100  2.0.1  412.7 
1.2.2  125.2  2.0.2  416.11 
1.2.3  125.4  2.0.3 417.9 
1.2.4  125.5.5  2.0.4 418.8 
1.3  312.1  3.0.4  523.10 
1.3.1 321.5  3.1  525 

  接下来测试 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/ 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);
Firefox版本号  最低限度的Gecko版本号 Firefox版本号  最低限度的Gecko版本号
1.0 1.7.5 3.5 1.9.1
1.5 1.8.0 3.6 1.9.2
2.0 1.8.1 4.0 2.0.0
3.0 1.9.0    

最后是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);
}

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.识别平台

  很多时候,只要知道呈现引擎就可以编写出适合的代码了,但在有些情况下,平台可能是必须关注的问题。三大主流平台: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]客户端检测的更多相关文章

  1. javascript客户端检测技术

    1. Firefox  Gecko是firefox的呈现引擎.当初的Gecko是作为通用Mozilla浏览器一部分开发的,而第一个采用Gecko引擎的浏览器是Netscape6: 我们可以使用用户代理 ...

  2. Javascript高级程序设计——客户端检测

    ECMAScript虽然是Javascript的核心,但是要在web中使用Javascript,那么BOM才是核心,BOM为我们提供了操作访问浏览器对象的借口, 但是由于BOM没有标准规范,导致存在不 ...

  3. 读书时间《JavaScript高级程序设计》四:BOM,客户端检测

    隔了一段时间,现在开始看第8章. 第8章:BOM BOM提供了很多对象,用于访问浏览器的功能.BOM的核心对象是window,它表示浏览器的一个实例. window对象是通过javascript访问浏 ...

  4. JavaScript浏览器检测之客户端检测

    客户端检测一共分为三种,分别为:能力检测.怪癖检测和用户代理检测,通过这三种检测方案,我们可以充分的了解当前浏览器所处系统.所支持的语法.所具有的特殊性能. 一.能力检测: 能力检测又称作为特性检测, ...

  5. JavaScript高级程序设计学习笔记第九章--客户端检测

    1.能力检测:能力检测的目标不是识别特定的浏览器,而是识别浏览器的能力.(我的理解就是识别浏览器能做什么不能做什么) 2.怪癖检测:目标是识别浏览器的特殊行为.但与能力检测确认浏览器支持什么能力不同, ...

  6. 《JAVASCRIPT高级程序设计》客户端检测

    web开发的理想状态之一是浏览器支持一组最常用的功能,但是在现实情况下,浏览器间的差异非常大,因此,为了兼容大部分的了浏览器,开发人员首先需要设计最通用的方案,然后再使用客户端检测的技术增强该方案.客 ...

  7. Javascript高级编程学习笔记(34)—— 客户端检测(3)用户代理检测

    用户代理检测 前面的文章介绍的是如何检测浏览器对某一功能的支持情况 但是在实践中我们有些时候免不了需要知道用户到底是用的什么浏览器对我们的站点进行访问 这也是统计用户行为的一部分 用户代理检测这种方式 ...

  8. Javascript高级编程学习笔记(32)—— 客户端检测(1)能力检测

    能力检测 浏览器厂商虽然在实现公共接口方面投入了大量的精力 但是每种浏览器仍旧存在许多差异 为了让网页能跨浏览器的运行,对浏览器差异做的兼容处理自然无法避免 其中最常用的也就是我们现在所说的能力检测 ...

  9. JavaScript功能检测技术和函数构造

    Javascript与很多编程语言不同,它不能够控制其运行环境.再写php代码时,只要在服务器端部署了正确的版本,那么程序就绝对能够运行,对于其他python或ruby后端语言来说,也不存在什么灰色区 ...

随机推荐

  1. Pytorch报错:cuda runtime error (59) : device-side assert triggered at /pytorch/aten/src/THC/generic/THCTensorMath.cu:26

    Pytorch报错:cuda runtime error (59) : device-side assert triggered at /pytorch/aten/src/THC/generic/TH ...

  2. 09 redis中布隆过滤器的使用

    我们在使用新闻客户端看新闻时,它会给我们不停地推荐新的内容,它每次推荐时要去重,去掉那些已经看过的内容.问题来了,新闻客户端推荐系统如何实现推送去重的? 会想到服务器记录了用户看过的所有历史记录,当推 ...

  3. ThreeJS 3d模型简介

    本文主要是对Threejs中加载模型的支持种类进行简单的知识科普. 3ds (.3ds) 3ds是3ds max通用储存文件格式.使用的范围更宽,可被更多的软件识别使用. amf (.amf) AMF ...

  4. SpringCloud 随笔

    目录 服务间通讯 统一配置中心 RabbitMQ Spring Cloud Stream 服务网关 Spring Cloud Zuul ++==(纯手打,代码可能有错!)==++ 服务间通讯 Rest ...

  5. vue项目中使用特殊字体

    项目开发中遇到要是有‘数字’字体的情况,样式如下 网上查了一下实现的方法很简单,而且具体的实现方式大致相同,可以参考以下几个链接: https://www.cnblogs.com/zhangnan35 ...

  6. golang在linux后台执行的方法

    go build ./index.go 会生成一个index的运行文件 nohup index & 后台运行index文件 killall index 你可能还要关闭index set GOA ...

  7. JAVA 1.6锁状态转换

    JVM 学不好 并发就学不好 面试问题 Object 有哪些方法 syn实现过程 wait notify 为什么要设计到Object上而不是接口?虽然可以 但是面向对象的思想 子类 object.wa ...

  8. mysql tinyint(1) 在java中被转化为boolean

    数据库表字段类型为:tinyint 长度为1 在java中对应的类型是boolean 查询时直接在页面展示成true或false 如果是2,3,4 这样的也是默认成true,非常不友好. 解决方案: ...

  9. Django项目上线的准备工作

    settings文件配置 添加上STATIC_ROOT = os.path.join(BASE_DIR, "/static/") 我的配置项目文件的最终 STATIC_URL = ...

  10. Django单表查询及其方法

    单表查询 前期准备 首先新建一个test的python文件,然后再manage.py中导入main语句及其下面的复制到新文件中 并导入django 写上django.setup() 就可以导入对应的m ...