McFog@がんばらない

a browser incompatibility behavior about jquery and unattached element

2013-06-23

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计算后都等于windowname是要获取的属性名,这里是display。chrome下ret拿到的值是空字符串,而火狐下拿到的是block,从而火狐会将这个值写入olddisplay,最终导致show的时候元素被加上display:block

观察&总结

最后我计算了getComputedStyle(document.createElement('div'), null),发现chrome基本上都是空,而火狐则填充了各种各样的默认属性(顺带一提IE9的行为和火狐类似)

unattached styles

究竟哪种行为才符合标准已经不重要,总之避免读取脱机元素的样式才能避开这里的问题,虽然脱机元素操作完再插入DOM是性能友好的做法,但浏览器兼容性多数情况下还是比性能更重要的

本次问题代码fiddle

chrome下显示inline-block,火狐/IE9则显示block

© 2018 McFog W. All rights reserved. がんばらない