javascript 特性检测并非浏览器检测


Posted in Javascript onJanuary 15, 2010

我大致翻译了部分文章,可能有理解错误的地方,敬请指正。值得一提的是,评论部分的争论亦值得一看。

特性检测
起初前端工程师们就极力反对浏览器检测,他们认为类似user-agent嗅探的方法是很不好的,理由是它并不是一种面向未来的代码,无法适应新版的浏览器。更好的做法是使用特性检测,就像这样:

if (navigator.userAgent.indexOf("MSIE 7") > -1){ 
//do something 
}

而更好的做法是这样:
if(document.all){ 
//do something 
}

这两种方式并不相同。前者是检测浏览器的特殊名称和版本;后者却是检测浏览器的特性。UA嗅探能够精确得到浏览器的类型和版本(至少能得知浏览器类型),而特性检测却是去确定浏览器是否拥有某个对象或者支持某个方法。注意这两者是完全不同的。
因为特性检测依赖于哪些浏览器支持,当出现新版本浏览器的时候需要繁琐的确认工作。例如DOM标准刚出现的时候,并不是所有浏览器都支持getElementById()方法,所以一开始代码可能是这样:
if(document.getElementById){ //DOM 
element = document.getElementById(id); 
} else if (document.all) { //IE 
element = document.all[id]; 
} else if (document.layers){ //Netscape < 6 
element = document.layers[id]; 
}

这是特性检测很好的一个例子,亮点在于当其它浏览器开始支持getElementById()方法时不必修改代码。
混合方式
后来前端工程师们考虑改进的写法,代码变化成这样:
//AVOID!!! 
if (document.all) { //IE 
id = document.uniqueID; 
} else { 
id = Math.random(); 
}

这个代码的问题是通过检测document.all属性来确定是否是IE。当确定是IE后,假定使用私有的document.uniqueID属性也是安全的。然而,目前所作的只是确定是否支持document.all,并非是去辨识浏览器是否为IE。仅仅支持document.all的话也不意味着document.uniqueID是可用的。
后来人们开始这样写,用下面那行代替上面的:
var isIE = navigator.userAgent.indexOf("MSIE") > -1;
//下面这行代替上面那行
var isIE = !!document.all;这些变化说明大家对“不要使用UA嗅探”存在误解——不再对浏览器的详细信息进行检测,取而代之的是通过特性的支持来推断。这种基于浏览器特性检测的方式非常不好。
后来前端们发现document.all并不可靠,更好的检测IE变为:
var isIE = !!document.all && document.uniqueID;这种实现方式陷入歧途。不仅需要费时费事地去识别浏览器所增加的特性支持,另外也不能确定其它浏览器开始支持相同的特性。
如果你认为这样的代码并未被广泛使用,那么看看来自于老版本的Mootools代码片段吧:
//from MooTools 1.1.2 
if (window.ActiveXObject) window.ie = window[window.XMLHttpRequest ? 'ie7' : 'ie6'] = true; 
else if (document.childNodes && !document.all && !navigator.taintEnabled) window.webkit = window[window.xpath ? 'webkit420' : 'webkit419'] = true; 
else if (document.getBoxObjectFor != null || window.mozInnerScreenX != null) window.gecko = true;

注意它是如何使用特性检测的。我可以指出它一系列的问题,比如通过检测window.ie会将ie8误认为ie7。
余波
随着浏览器的快速发展,使用特性检测变得越来越困难和不可靠。但是Mootools 1.2.4仍然使用这一方法,例如:getBoxObjectFor()。
//from MooTools 1.2.4 
var Browser = $merge({ 
    Engine: {name: 'unknown', version: 0}, 
    Platform: {name: (window.orientation != undefined) ? 'ipod' : (navigator.platform.match(/mac|win|linux/i) || ['other'])[0].toLowerCase()}, 
    Features: {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)}, 
    Plugins: {}, 
    Engines: { 
        presto: function(){ 
            return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); 
        }, 
        trident: function(){ 
            return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4); 
        }, 
        webkit: function(){ 
            return (navigator.taintEnabled) ? false : ((Browser.Features.xpath) ? ((Browser.Features.query) ? 525 : 420) : 419); 
        }, 
        gecko: function(){ 
            return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18); 
        } 
    } 
}, Browser || {});

应该怎么做?
特性检测是个应该避免的方法,尽管直接进行特性检测是个很好的方法,并且大部分情况下能满足需求。一般只要在检测前知道这个特性是否被实现即可,而不会去考虑它们之间的关系。
我并非是说永远不使用浏览器特性检测而是基于UA嗅探,因为我相信它还是有很多用途的,然而我不相信它有很多合理的用途。如果你考虑UA嗅探的话,请先贯彻这一思想:唯一安全的方式是针对特定浏览器的特定版本,超出范围之外都是不可靠的——例如新出的浏览器版本。其实这样做也是个明智的办法,因为相较于向前兼容不确定的新版本而言,向后兼容老版本是最简单的做法。
Javascript 相关文章推荐
ExtJs使用IFrame的实现代码
Mar 24 Javascript
父节点获取子节点的字符串示例代码
Feb 26 Javascript
使用jQuery判断Div是否在可视区域的方法 判断div是否可见
Feb 17 Javascript
Vue.js实现移动端短信验证码功能
Mar 29 Javascript
Bootstrap进度条与AJAX后端数据传递结合使用实例详解
Apr 23 Javascript
使用Require.js封装原生js轮播图的实现代码
Jun 15 Javascript
Vue项目中quill-editor带样式编辑器的使用方法
Aug 08 Javascript
vue中uni-app 实现小程序登录注册功能
Oct 12 Javascript
微信小程序工具函数封装
Oct 28 Javascript
Layui表格监听行单双击事件讲解
Nov 14 Javascript
深入浅析React中diff算法
May 19 Javascript
15个值得收藏的JavaScript函数
Sep 15 Javascript
javascript 构建一个xmlhttp对象池合理创建和使用xmlhttp对象
Jan 15 #Javascript
extjs 为某个事件设置拦截器
Jan 15 #Javascript
利用onresize使得div可以随着屏幕大小而自适应的代码
Jan 15 #Javascript
javascript 不间断的图片滚动并可点击
Jan 15 #Javascript
Span元素的width属性无效果原因及解决方案
Jan 15 #Javascript
javascript实现的基于金山词霸网络翻译的代码
Jan 15 #Javascript
JQuery 引发两次$(document.ready)事件
Jan 15 #Javascript
You might like
利用PHP制作简单的内容采集器的原理分析
2008/10/01 PHP
php 无极分类(递归)实现代码
2010/01/05 PHP
php中用socket模拟http中post或者get提交数据的示例代码
2013/08/08 PHP
数组任意位置插入元素,删除特定元素的实例
2017/03/02 PHP
PHPMAILER实现PHP发邮件功能
2018/04/18 PHP
使用composer命令加载vendor中的第三方类库 的方法
2019/07/09 PHP
使用Git实现Laravel项目的自动化部署
2019/11/24 PHP
JavaScript实现自己的DOM选择器原理及代码
2013/03/04 Javascript
node.js中的fs.fsync方法使用说明
2014/12/15 Javascript
Jquery树插件zTree用法入门教程
2015/02/17 Javascript
js实现兼容性好的微软官网导航下拉菜单效果
2015/09/07 Javascript
属于你的jQuery提示框(Tip)插件
2016/01/20 Javascript
jQuery添加和删除输入文本框标签代码
2016/05/20 Javascript
JS中innerHTML和pasteHTML的区别实例分析
2016/06/22 Javascript
原生Javascript插件开发实践
2017/01/09 Javascript
JS实现根据密码长度显示安全条功能
2017/03/08 Javascript
vue 中引用gojs绘制E-R图的方法示例
2018/08/24 Javascript
[40:05]DOTA2上海特级锦标赛A组小组赛#1 EHOME VS MVP.Phx第一局
2016/02/25 DOTA
在Python中操作字符串之replace()方法的使用
2015/05/19 Python
详解Python中的各种函数的使用
2015/05/24 Python
Python检测网站链接是否已存在
2016/04/07 Python
分享一个可以生成各种进制格式IP的小工具实例代码
2017/07/28 Python
Python闭包函数定义与用法分析
2018/07/20 Python
Python爬取商家联系电话以及各种数据的方法
2018/11/10 Python
python 批量修改 labelImg 生成的xml文件的方法
2019/09/09 Python
解决import tensorflow导致jupyter内核死亡的问题
2021/02/06 Python
美国最灵活的移动提供商:Tello
2017/07/18 全球购物
城野医生官方海外旗舰店:风靡亚洲毛孔收敛水
2018/04/26 全球购物
六一儿童节活动策划方案
2014/01/27 职场文书
书法兴趣小组活动总结
2014/07/07 职场文书
办公室主任个人对照检查材料思想汇报
2014/10/11 职场文书
党的群众路线教育实践活动个人对照检查材料(校长)
2014/11/05 职场文书
2016年优秀教师先进事迹材料
2016/02/26 职场文书
深度学习tensorflow基础mnist
2021/04/14 Python
Spring Boot 启动、停止、重启、状态脚本
2021/06/26 Java/Android
Tomcat 与 maven 的安装与使用教程
2022/06/16 Servers