[Javascript]客户端检测
客户端检测是一种行之有效的开发策略。但不到万不得已,就不要使用客户端检测。先设计通用的方案,然后根据浏览器之间的差异和各自的怪癖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/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]客户端检测的更多相关文章
- javascript客户端检测技术
1. Firefox Gecko是firefox的呈现引擎.当初的Gecko是作为通用Mozilla浏览器一部分开发的,而第一个采用Gecko引擎的浏览器是Netscape6: 我们可以使用用户代理 ...
- Javascript高级程序设计——客户端检测
ECMAScript虽然是Javascript的核心,但是要在web中使用Javascript,那么BOM才是核心,BOM为我们提供了操作访问浏览器对象的借口, 但是由于BOM没有标准规范,导致存在不 ...
- 读书时间《JavaScript高级程序设计》四:BOM,客户端检测
隔了一段时间,现在开始看第8章. 第8章:BOM BOM提供了很多对象,用于访问浏览器的功能.BOM的核心对象是window,它表示浏览器的一个实例. window对象是通过javascript访问浏 ...
- JavaScript浏览器检测之客户端检测
客户端检测一共分为三种,分别为:能力检测.怪癖检测和用户代理检测,通过这三种检测方案,我们可以充分的了解当前浏览器所处系统.所支持的语法.所具有的特殊性能. 一.能力检测: 能力检测又称作为特性检测, ...
- JavaScript高级程序设计学习笔记第九章--客户端检测
1.能力检测:能力检测的目标不是识别特定的浏览器,而是识别浏览器的能力.(我的理解就是识别浏览器能做什么不能做什么) 2.怪癖检测:目标是识别浏览器的特殊行为.但与能力检测确认浏览器支持什么能力不同, ...
- 《JAVASCRIPT高级程序设计》客户端检测
web开发的理想状态之一是浏览器支持一组最常用的功能,但是在现实情况下,浏览器间的差异非常大,因此,为了兼容大部分的了浏览器,开发人员首先需要设计最通用的方案,然后再使用客户端检测的技术增强该方案.客 ...
- Javascript高级编程学习笔记(34)—— 客户端检测(3)用户代理检测
用户代理检测 前面的文章介绍的是如何检测浏览器对某一功能的支持情况 但是在实践中我们有些时候免不了需要知道用户到底是用的什么浏览器对我们的站点进行访问 这也是统计用户行为的一部分 用户代理检测这种方式 ...
- Javascript高级编程学习笔记(32)—— 客户端检测(1)能力检测
能力检测 浏览器厂商虽然在实现公共接口方面投入了大量的精力 但是每种浏览器仍旧存在许多差异 为了让网页能跨浏览器的运行,对浏览器差异做的兼容处理自然无法避免 其中最常用的也就是我们现在所说的能力检测 ...
- JavaScript功能检测技术和函数构造
Javascript与很多编程语言不同,它不能够控制其运行环境.再写php代码时,只要在服务器端部署了正确的版本,那么程序就绝对能够运行,对于其他python或ruby后端语言来说,也不存在什么灰色区 ...
随机推荐
- HDU5124lines题解-堆+贪心的一个新方法
题目链接 https://cn.vjudge.net/problem/HDU-5124 胡扯 感觉说新方法好像有点不太好,但是翻了十几篇博客都是清一色离散化之类的... 为什么会做这道题呢?因为前几天 ...
- JS笛卡尔积算法与多重数组笛卡尔积实现方法示例
js 笛卡尔积算法的实现代码,据对象或者数组生成笛卡尔积,并介绍了一个javascript多重数组笛卡尔积的例子,以及java实现笛卡尔积的算法与实例代码. 一.javascript笛卡尔积算法代码 ...
- vue学习(9)-路由守卫
全局守卫 你可以使用 router.beforeEach 注册一个全局前置守卫: const router = new VueRouter({ ... }) router.beforeEach(( ...
- c#如何使用MemoryStream和BinaryFormatter进行对象的序列化和返序列化
1 下面是我写的一个序列化的类 public static class ObjSerialize { /// <summary> /// 将对象数组obj序列化,内存中的缓冲区的数据序列化 ...
- Java秒杀实战 (四)JMeter压测
转自:https://blog.csdn.net/qq_41305266/article/details/81071278. 一.JMeter入门 下载链接 http://jmeter.apache. ...
- 【SpringBoot】自动配置
一.取值 1.1 @Value 1.2 ConfigurationProperties 二.导入配置文件 2.1 @PropertySource 三.配置文件的加载 3.1 默认的加载顺序 3.2 外 ...
- Linux小试牛刀
1.统计出/etc/passwd文件中其默认shell为非/sbin/nologin的用户个数,并将用户都显示出来 [root@centos7data]#getent passwd | grep -v ...
- javascript typeof instanceof
typeof用以获取一个变量或者表达式的类型,typeof一般只能返回如下几个结果: number,boolean,string,function(函数),object(NULL,数组,对象),und ...
- JVM命令jps
jps是JVM的一个常用命令,类似linux中的ps命令.jps是查看java进程信息的命令:ps是查看linux系统中进程的命令 格式 jps [ options ] [ host ...
- CVE-2018-10933 LibSSH auth bypass
漏洞原理 认证实现错误, 认证分为多个步骤,可以直接跳到成功的步骤 A vulnerability was found in libssh's server-side state mach ...