a browser incompatibility behavior about jquery and unattached element
2013-06-23
摘要
- jQuery.fn.hide 会读取元素的display样式
- 读取尚未插入dom树的脱机元素的样式时,不同浏览器行为不同
场景
在一段显示错误提示的代码中,发现firefox下显示异常,错误提示tips的宽度没有自适应内容而是占满了整个容器。inspect后发现,本应是由CSS控制的display:inline-block
被元素的style="display:block"
覆盖,chrome下没有这个问题
jQuery.fn.show
断点调查后发现在jQ的show
方法中有这样一段代码
elem.style.display = jQuery._data( elem, "olddisplay" ) || "";
也就是说jQ为了hide和show后能够保持原有的display属性,会将元素原来的display属性暂存起来,这样一个原本inline-block
的元素经过hide和show之后不会被改成block
,firefox下问题tips元素的jQuery._data( elem, "olddisplay" )
值是block
,所以show的行为没有问题,问题应当是hide的时候这个计算算错了
jQuery.fn.hide
再来看hide的相关代码,中规中矩,用jQuery.css
获取display属性
display = jQuery.css( elem, "display" );
if ( display !== "none" && !jQuery._data( elem, "olddisplay" ) ) {
jQuery._data( elem, "olddisplay", display );
}
继续追踪后在jQuery.css
的实现中发现了浏览器不一致的来源
if ( (defaultView = elem.ownerDocument.defaultView) &&
(computedStyle = defaultView.getComputedStyle( elem, null )) ) {
ret = computedStyle.getPropertyValue( name );
这里的elem是一个未加入dom树的“脱机”元素,defaultView
火狐和chrome计算后都等于window
,name
是要获取的属性名,这里是display。chrome下ret
拿到的值是空字符串,而火狐下拿到的是block
,从而火狐会将这个值写入olddisplay
,最终导致show的时候元素被加上display:block
观察&总结
最后我计算了getComputedStyle(document.createElement('div'), null)
,发现chrome基本上都是空,而火狐则填充了各种各样的默认属性(顺带一提IE9的行为和火狐类似)
究竟哪种行为才符合标准已经不重要,总之避免读取脱机元素的样式才能避开这里的问题,虽然脱机元素操作完再插入DOM是性能友好的做法,但浏览器兼容性多数情况下还是比性能更重要的
本次问题代码fiddle
chrome下显示inline-block,火狐/IE9则显示block