浏览器特性检测即通过探测对象是否拥有某个属性或者函数,或者通过其他的编码探测方式,来决定其是否支持某一功能、特性。其最经典的运用莫过于通用的addEvent函数:

function addEvent(element, type, handler) {if (element.attachEvent) { //IE8及以下浏览器element.attachEvent('on' + type, handler);}else { //W3C标准浏览器element.addEventListener(type, handler, false);}
};

函数可以通过检测attachEvent函数是否存在,以决定使用attachEvent或者addEventListener,这也是最简单的一种特性检测,因而通常在需要时才进行实时的检测。另一种特性检测由于检测的过程较为麻烦,因此会预先完成检测,将检测的结果(通常是Boolean类型)保存在某个变量中。

本文的主要目标是分析、说明在jQuery1.4中浏览器特性检测新增的内容,同时加深浏览器兼容性方面几个细节的记忆。

jQuery1.4主要增加了以下几个浏览器特性标识,本文针对它们一一进行分析:

checkOn
1.4版本引入,决定没有设置value值的checkbox是否有默认的value值"on"。
optSelected
1.4.3版本引入,决定select元素的第一个option元素是否会默认被选中。
optDisabled
1.4.3版本引入,决定当select元素设置为disabled后,其所有option子元素是否也会被设置为disabled。
checkClone
1.4.1版本引入,决定对DocumentFragment使用cloneNode函数时是否会将radio和checkbox的checked属性保留。
inlineBlockNeedsLayout
1.4.3版本引入,决定在IE下一个block元素拥有hasLayout属性并有display: inline;时,是否会按inline-block显示。
shrinkWrapBlocks
1.4.3版本引入,决定在IE下一个元素拥有hasLayout属性和固定的width/height时,是否不会被子元素撑大。
reliableHiddenOffsets
1.4.3版本引入,决定一个td或th元素设置为display: none;时,是否还有offsetHeight。

checkOn

使用以下代码可以检测该特性:

<input id="checkOn" type="checkbox" />
<script type="text/javascript">alert(document.getElementById('checkOn').value);
</script>

以下为各浏览器中运行结果:

IE6 on
IE7 on
IE8 on
IE9 beta on
Firefox 3.6 on
Chrome 7 [空字符串]
Safari 5 on

经测试,除Chrome外,所有浏览器都会给没有value的checkbox一个默认的value值"on"。

该特性被jQuery用来获取checkbox和radio的值,兼容的判断语句如下:

//不支持checkOn的浏览器都不存在property/attribute混用问题,因此需要明确使用getAttribute
return support.checkOn ? element.value :  (element.getAttribute('value') === null ? 'on' : element.value);

optSelected

使用以下代码可以检测该特性:

<select id="optSelected">
</select>
<script type="text/javascript">var select = document.getElementById('optSelected'),option = document.createElement('option');select.appendChild(option);alert(option.selected);
</script>

以下为各浏览器中运行结果:

IE6 false
IE7 false
IE8 false
IE9 beta false
Firefox 3.6 true
Chrome 7 true
Safari 5 false

经测试,IE系列和Safari使用appendChild对空的select元素添加一个option后,该option的selected属性不会被默认设置为true。

该问题引起的BUG描述如下:

部分浏览器在获取option的selected属性时,会错误地返回false。

该问题的解决方案是在访问selected属性时,先访问其父级select元素的selectedIndex属性,强迫浏览器计算option的selected属性,以得到正确的值。需要注意的是option元素的父元素不一定是select,也有可能是optgroup。具体代码如下:

if (!support.optSelected) {var parent = option.parentNode;parent.selectedIndex;//处理optgroup时的情况if (parent.parentNode) {parent.parentNode.selectedIndex;}
}
return option.selected;

optDisabled

使用以下代码可以检测该特性:

<select id="optDisabled" disabled="disabled"><option></option>
</select>
<script type="text/javascript">var select = document.getElementById('optDisabled'),option = select.getElementsByTagName('option')[0];alert(option.disabled);
</script>

以下为各浏览器中运行结果:

IE6 false
IE7 false
IE8 false
IE9 beta false
Firefox 3.6 false
Chrome 7 false
Safari 5 true

经测试,Safari会将设置了disabled的select中的option也同样设置上disabled。

这个特性用来获取select元素的value值,特别是当select渲染为多选框时,需要注意从中去除disabled的option元素,但在Safari中,获取被设置为disabled的select的值时,由于所有option元素都被设置为disabled,会导致无法获取值。

因此有optDisabled(true表示option不会被自动设置disabled)后,可以有这样的代码:

//如果optDisabled为true,则disabled属性返回的是option的真实状态
//否则判断disabled属性是否为null
var disabled = support.optDisabled ? option.disabled : option.getAttribute('disabled') !== null;
if (!disabled) {return option.value;
}

checkClone

使用以下代码可以检测该特性:

<div id="checkClone"><input type="radio" name="checkClone" checked="checked" />
</div>
<script type="text/javascript">var fragment = document.createDocumentFragment(),div = document.getElementById('checkClone'),radio = div.getElementsByTagName('input')[0];fragment.appendChild( radio );alert(fragment.cloneNode(true).cloneNode(true).lastChild.checked);
</script>

需要注意的是,重现这个问题,需要给input显式地指定一个name属性,并且在复制fragment对象时连续调用2次cloneNode函数。

以下为各浏览器中运行结果:

IE6 true
IE7 true
IE8 true
IE9 beta true
Firefox 3.6 true
Chrome 7 true
Safari 5 true
Safari 4 false

由结果可以看出,该问题出现在Safari 4中,并且已经在Safari 5得到修复,介于Safari在市场中的占有率以及版本较老的原因,这个问题确实不需要太多的重视。

这个特性的使用场合极少,在开发中几乎不会有如此严格的环境(对DocumentFragment连续调用2次cloneNode),在jQuery中,该特性用做buildFragment这个内部函数中的缓存功能,jQuery会对比较简单的创建DOM元素的字符串的创建结果缓存到DocumentFragment中,但当遇到创建radio时,如果cloneNode为false,则强制不进行缓存。

inlineBlockNeedsLayout

这是一个历史久远的问题,IE7以下版本并不支持display: inline-block;样式,而是使用display: inline;并通过其他样式触发其hasLayout形成一种伪inline-block的状态(具体请点击这里)。

inline-block与inline的一个重要区别在于,inline-block的元素可以显式地设置宽和高,因此可以用以下代码检测该特性:

<div id="inlineBlockNeedsLayout" style="width: 1px; padding-left: 1px; display: inline;zoom: 1;">
</div>
<script type="text/javascript">var div = document.getElementById('inlineBlockNeedsLayout');alert(div.offsetWidth);
</script>

以下为各浏览器中运行结果:

IE6 2
IE7 2
IE8 1
IE9 beta 1
Firefox 3.6 1
Chrome 7 0
Safari 5 0

对于inline元素,width样式是无效的,在该测试中,webkit系浏览器均获取了0,IE8以上版本及Firefox获取了1,只有IE7及以下版本同时计算了width和padding-left,得到了2px的宽度。

这个功能可以用于设置元素的css样式,当需要设置为inline-block时,针对IE7及以下浏览器可以同时设置display: inline;zoom: 1;来模拟效果,核心代码如下:

if (name == 'display' && value == 'inline-block') {if (support.inlineBlockNeedsLayout) {element.style.display = 'inline';element.style.zoom = 1;}else {element.style.display = value;}
};

当然这样直接这样使用肯定是有问题的,当需要获取display样式的时候怎么办呢?同时判断zoom和display吗?并且hasLayout会引起一些其他的问题。

因此,jQuery只将该特性用于动画效果,当需要对width和height进行动画,并且元素是inline时,首先设置为(伪)inline-block状态,动画结束后将相关样式恢复。

shrinkWrapBlocks

这个问题的详细解释可以参考此处,使用以下代码可以检测该特性:

<div id="shrinkWrapBlocks" style="width: 1px; zoom: 1;"><div style="width: 4px;"></div>
</div>
<script type="text/javascript">var div = document.getElementById('shrinkWrapBlocks'),inner = div.getElementsByTagName('div')[0];alert(div.offsetWidth);
</script>

以下为各浏览器中运行结果:

IE6 4
IE7 1
IE8 1
IE9 beta 1
Firefox 3.6 1
Chrome 7 1
Safari 5 1

测试结果表明,IE6即使显式设定了宽度,在触发了hasLayout的情况下,其大小会受子元素的影响而被撑大。

jQuery将该特性用于动画效果,为了动画过程中改变一个元素的width/height时,其子元素不会溢出,jQuery做了以下几步:

  1. 保存元素当前的overflow、overflow-x、overflow-y三个样式。
  2. 将元素设置为inline-block以便修改width/height值。
  3. 将元素的overflow设为hidden,防止子元素溢出或当前元素被子元素撑开(IE6)。
  4. 在动画结束后,确保元素不会被子元素撑开(shrinkWrapBlocks为true)的情况下,才恢复overflow样式。

reliableHiddenOffsets

这个问题在上两天工作中遇到,刚好jQuery1.4.3升级了这方面的内容,使用以下代码可以检测该特性:

<table id="reliableHiddenOffsets"><tbody><tr><td style="display: none;"></td><td>abcd</td></tr></tbody>
</table>
<script type="text/javascript">var table = document.getElementById('reliableHiddenOffsets'),td = table.getElementsByTagName('td')[0];alert(td.offsetHeight);
</script>

以下为各浏览器中运行结果:

IE6 0
IE7 0
IE8 21
IE9 beta 0
Firefox 3.6 0
Chrome 7 0
Safari 5 0

只有IE8存在这个问题,那当td元素的display为none时,其高度依旧会受其所在行的高度的影响,而不是0。

这个问题的存在根本上导致了对元素可见性的判定出现差错,原本判断一个元素是否隐藏的代码是这样的:

function isHidden(element) {return element.offsetWidth == 0 || element.offsetHeight == 0;
};

因为这个BUG的出现,上面的函数对于td元素失去了效果,因此需要改进为:

function isVisible(element) {return (element.offsetWidth == 0 && element.offsetHeight == 0) || (!support.reliableHiddenOffsets && getStyle(element, 'display') == 'none');
};

阅读jQuery源码的时候,会发现这一段的判断里多了一句element.style.display,这一句是用来判断元素有display值才去取来看看是不是none的,以免获取运行时样式的开销。

结语

  1. 特性检测确实很有用,有时比浏览器版本嗅探更佳可靠,但检测某些特性相当麻烦,不是必要的时候不如用浏览器嗅探。
  2. jQuery对特性的命名真让人想砍了他们团队。
  3. 有些特性可以重现的浏览器版本之低令人惊讶,在多数项目中完全可以不考虑,如checkClone。jQuery本身为了兼容做了太多的假设,个人认为有一些完全可以抛弃,比如以后会说的getBoundingClientRect问题。
  4. 另外还有2个关于事件上的特性检测,由于事件的特性检测是一个通用的话题,会有今后专门写文讲述,因此就不在本文中赘述了。
  5. jQuery每一个小版本的改进都很大,特别在细节方面,这些都是要通过阅读源码不断发掘的,前端的世界就是这么多变(叹)。
  6. 本文所用的示例可以点此查看,具体可以查看源代码,本文所述的各个问题/BUG都没在网上找到比较权威的说明,还请见谅!

本文永久地址:http://www.otakustay.com/feature-detection-jquery1-4/

细说浏览器特性检测(1)-jQuery1.4添加部分相关推荐

  1. 细说浏览器特性检测(2)-通用事件检测

    在上一篇中介绍了jQuery1.4版本新增的几个浏览器特性检测方案和具体的目的,本文将以事件为中心,介绍一个较为完整.通用的事件检测方案. 事件检测,即检测某一事件在不同的浏览器中是否存在(可用),这 ...

  2. php事件检测,细说浏览器特性检测(2)-通用事件检测_jquery

    在上一篇中介绍了jQuery1.4版本新增的几个浏览器特性检测方案和具体的目的,本文将以事件为中心,介绍一个较为完整.通用的事件检测方案. 事件检测,即检测某一事件在不同的浏览器中是否存在(可用),这 ...

  3. 浏览器对HTML5特性检测工具Modernizr

    最近在做公司移动端运营的项目,需求中多处地方都会涉及动画.相信很多前端开发都会有这种感触,对CSS3中的动画属性很熟悉,但是由于对动画运动过程的理解不深入,经常只能望而止步.CSS3中动画这在Web页 ...

  4. html5 --- 特性检测

    使用Moderniz库可以对H5的特性进行检测,下载网址: https://modernizr.com // 在HTML 中的head标签中导入 <script src="/moder ...

  5. 那些开源程序中让人叹为观止的代码 - 1 浏览器特性判断

    浏览器特性判断 解决问题:判断某事件/方法在当前浏览器中是否支持 开源程序:Modernizr 众所周知,各个不同的浏览器对于代码渲染的实现也各自为政百花齐放,虽然有W3C在维护着标准,但是由于市面上 ...

  6. 火狐浏览器firefox检测不到U盾证书

    火狐浏览器firefox检测不到U盾证书 近来改用火狐浏览器上网,但想用网上银行支付的时候发现,火狐浏览器检测不到农行K宝U盾的证书,如何解决呢?今天就和大家分享分享这个办法. 第一步:打开火狐浏览器 ...

  7. 深度篇——目标检测史(二) 细说 R-CNN 目标检测

    返回主目录 返回 目标检测史 目录 上一章:深度篇--目标检测史(一) 关于 古典 目标检测 下一章:深度篇--目标检测史(三)  细说 SPP-Net 目标检测 论文地址:<Rich feat ...

  8. 深度篇——目标检测史(七) 细说 YOLO-V3目标检测 之 代码详解

    返回主目录 返回 目标检测史 目录 上一章:深度篇--目标检测史(六) 细说 YOLO-V3目标检测 下一章:深度篇--目标检测史(八) 细说 CornerNet-Lite 目标检测 论文地址:< ...

  9. js浏览器和浏览器插件检测的方法总结

    文/玄魂 前言 首次面对题目所示的需求的时候,头脑中没有任何概念,于是搜索,所有的中文结果都不是很满意.所幸老外的几篇文章还是很有参考价值,虽然最终没有解决我的问题,但是我还是把这几篇文章的内容作了抽 ...

最新文章

  1. C++11中头文件type_traits介绍
  2. 配置虚拟机和网络配置
  3. MVC的概念及MVC 3.0开发环境
  4. python pyside简单布局_python – pyside显示/隐藏布局
  5. 一个不错的报表工具 open flash chart 2
  6. [Objective-c 基础 - 2.1] 封装
  7. Mac下显示隐藏文件
  8. 区块链系列教程之:比特币中的挖矿
  9. Linux加密框架 crypto 算法模板
  10. JEasyPoi 2.1.4 (Jeecg订制) 版本发布,Excel 和 Word 简易工具类
  11. 开源等于免费吗?用事实来说话
  12. 21 个编程的热门/冷门趋势
  13. web中ajax实现二级联动,Ajax实现城市二级联动(一)
  14. 关于Jbulder2006的问题
  15. 华为鸿蒙系统再公测,安卓再见!华为正式发布鸿蒙系统公测版!
  16. 关于嵌入式系统的启动
  17. 华为自带计算机怎么算平方立方,智能家庭中心:华为荣耀立方体验评测
  18. Python序列 数据类型 创建方式 Tuple元组 Str字符串 List列表 dict字典 Set集合 range,zip,map,enumerate
  19. 安卓开发入门教程!终于有人把安卓程序员必学知识点全整理出来了,附答案
  20. 算法谜题——三个水壶问题

热门文章

  1. 【转】小木虫分享免费文献获得方法
  2. Win7下使用toad连接oracle出现can't initialize OCI -1
  3. spring-boot-devtools热加载不起作用
  4. 写专业书确能帮助自己快速提升——写在我的书出版半年后,同时和大家分享选书的方式...
  5. Python Day25
  6. APPIUM+Python+HTMLTestRunner(转)
  7. Html5+NodeJS——拖拽多个文件上传到服务器
  8. Sublime2 配置python 和 c++
  9. 原来真的不会用指针[*p++]
  10. MVC中如何实现本地化的解决方案