IMG 元素为拥有内在宽度的内联替换元素,CSS 2.1 第10章明确规定了内联替换元素的宽度算法: 如果元素的 'width' 和 'height' 特性计算值为 'auto' 且元素拥有内在宽度,则使用该内在宽度作为 'width' 的使用值。
关于内联替换元素的宽度计算的更多信息,请参考 CSS 2.1 10.3.2 Inline, replaced elements。
'min-width' 和 'max-width' 限定内容宽度的范围。定义值如下:
下述算法描述了这两个特性如何影响 'width' 特性的使用值:
关于 'min-width' 和 'max-width' 的更多信息,请参考 CSS 2.1 10.4 Minimum and maximum widths: 'min-width' and 'max-width'。
shrink-to-fit 可以理解为 "收缩至合适" ,代表了浏览器对于计算后宽度为 'auto' 时的非替换浮动元素宽度计算所要遵循的标准。此外除了非替换浮动元素,对非替换绝对定位元素、非替换行内块元素的宽度为 auto 时也遵循此计算方式。
根据 W3C CSS2.1 规范中的描述,shrink-to-fit 的宽度计算方法与 'table-layout' 特性为 'auto'(即自动表格布局)时对于单元格的宽度计算方法类似。大致为:
综上所述:
shrink-to-fit 的宽度 = min ( max (首选最小宽度, 可用宽度) , 首选宽度)
关于 shrink-to-fit 的更多资料,请参考 CSS2.1 规范 10.3.5 Floating, non-replaced elements 中的内容。
当 IMG 元素没有设置 'width' 特性且设置了值单位为百分比的 'min-width' 或 'max-width' 特性, 则在各浏览器中该 IMG 元素的包含块的 'shrink-to-fit' 算法存在差异。
该问题可能导致在视觉展现上各浏览器存在差异,也可能造成页面布局混乱。
所有浏览器 |
---|
该问题的核心原因在于各浏览器在对元素采用 'shrink-to-fit' 算法计算宽度的时候, 选取的首选最小宽度和首选宽度不同,因此导致了计算结果的差异。
对于 IE8(S) Firefox Chrome Safari Opera,主要差异来源于首选最小宽度的取值;而 IE7(S) 则比较特殊,下面会单独讨论它的处理方式。 由于 IE6 IE7(Q) IE8(Q) 不支持 'min-width' 和 'max-width' 特性,因此这些浏览器不在本文讨论范围之列。
分析以下代码:
<!doctype html> <html> <head> <style type="text/css"> .abs { position : absolute; } .cont { width:40px; background:red; padding:5px; position:absolute; left:100px; } .stf { float:left; border:5px solid; padding:5px; background:gold; } </style> </head> <body style="background:white;font:12px Arial;"> <div style="position:absolute;top:0;">min-width:0%;</div> <div class="cont" style="top:0;"> <div id="stf1" class="stf"> <img id="img1" src="google_small.gif" style="min-width:0%;" /> </div> </div> <div style="position:absolute;top:100px;">max-width:0%;</div> <div class="cont" style="top:100px;"> <div id="stf2" class="stf"> <img id="img2" src="google_small.gif" style="max-width:0%;" /> </div> </div> <div style="position:absolute;top:200px;">min-width:50%;</div> <div class="cont" style="top:200px;"> <div id="stf3" class="stf"> <img id="img1" src="google_small.gif" style="min-width:50%;" /> </div> </div> <div style="position:absolute;top:300px;">max-width:50%;</div> <div class="cont" style="top:300px;"> <div id="stf4" class="stf"> <img id="img2" src="google_small.gif" style="max-width:50%;" /> </div> </div> <div style="position:absolute;top:400px;">min-width:150%;</div> <div class="cont" style="top:400px;"> <div id="stf5" class="stf"> <img id="img1" src="google_small.gif" style="min-width:150%;" /> </div> </div> <div style="position:absolute;top:500px;">max-width:150%;</div> <div class="cont" style="top:500px;"> <div id="stf6" class="stf"> <img id="img2" src="google_small.gif" style="max-width:150%;" /> </div> </div> <div id="info" style="position:absolute;top:600px;"></div> <script type="text/javascript"> window.onload = function(){ var $ = function(id){return document.getElementById(id);}; function info(msg){ $("info").innerHTML += "computed width : " + msg + "px<br />"; } function getComputedStyle(id){ return window.getComputedStyle ? window.getComputedStyle($(id),null) : $(id).currentStyle; } var cs1 = getComputedStyle("stf1"), cs2 = getComputedStyle("stf2"), cs3 = getComputedStyle("stf3"), cs4 = getComputedStyle("stf4"), cs5 = getComputedStyle("stf5"), cs6 = getComputedStyle("stf6"); info($("stf1").offsetWidth - parseInt(cs1.paddingLeft) - parseInt(cs1.paddingRight) - parseInt(cs1.borderLeftWidth) - parseInt(cs1.borderRightWidth)); info($("stf2").offsetWidth - parseInt(cs2.paddingLeft) - parseInt(cs2.paddingRight) - parseInt(cs2.borderLeftWidth) - parseInt(cs2.borderRightWidth)); info($("stf3").offsetWidth - parseInt(cs3.paddingLeft) - parseInt(cs3.paddingRight) - parseInt(cs3.borderLeftWidth) - parseInt(cs3.borderRightWidth)); info($("stf4").offsetWidth - parseInt(cs4.paddingLeft) - parseInt(cs4.paddingRight) - parseInt(cs4.borderLeftWidth) - parseInt(cs4.borderRightWidth)); info($("stf5").offsetWidth - parseInt(cs5.paddingLeft) - parseInt(cs5.paddingRight) - parseInt(cs5.borderLeftWidth) - parseInt(cs5.borderRightWidth)); info($("stf6").offsetWidth - parseInt(cs6.paddingLeft) - parseInt(cs6.paddingRight) - parseInt(cs6.borderLeftWidth) - parseInt(cs6.borderRightWidth)); } </script> </body></html>
以上代码分别测试了 IMG 元素的 'min-width' 和 'max-width' 特性值为 0%、50% 和 150% 的情况下各浏览器如何计算其包含块的宽度, 页面底部使用脚本依次得出这些包含块宽度的最终计算值。图片 "google_small.gif" 内在宽度为 114px,DIV.cont 的宽度为 40px, DIV.stf 为浮动元素,它的 'border-width' 和 'padding' 均为 5px,其宽度需要使用 'shrink-to-fit' 算法决定。根据算法公式,我们能得到可用宽度均为 20px。
各浏览器中表现如下:
IE8(S) Firefox Opera | Chrome Safari | IE7(S) |
---|---|---|
修改 DIV.cont 的宽度为 200px,重新计算各包含块宽度,这时我们能得到可用宽度为 180px,各浏览器中表现如下:
IE8(S) Firefox Chrome Safari Opera | IE7(S) |
---|---|
综合以上测试结果,我们可以得出在 IE8(S) Firefox Chrome Safari Opera 下设置了 'min-width' 或 'max-width' 值单位为百分比的 IMG 元素的包含块的 'shrink-to-fit' 算法:
IE8(S) Firefox Opera | Chrome Safari |
---|---|
min( max(图片内在宽度, 可用宽度), 图片内在宽度)1 | min( max(0, 可用宽度), 图片内在宽度) |
注1: 本文中可用宽度分别为 20px 和 180px,图片内在宽度为 114px。
从源码中我们也可以发现 Webkit 在计算 'shrink-to-fit' 的首选最小宽度的时候,当遇到 IMG 元素的 'min-width' 和 'max-width' 值单位为百分比的时候, 会直接将首选最小宽度置为0,这和我们得出的结论是一致的。
关键代码位于 RenderImage.cpp 文件的最底部:
void RenderImage::calcPrefWidths() { ASSERT(prefWidthsDirty()); int borderAndPadding = borderAndPaddingWidth(); m_maxPrefWidth = calcReplacedWidth(false) + borderAndPadding; if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) m_maxPrefWidth = min(m_maxPrefWidth, style()->maxWidth().value() + (style()->boxSizing() == CONTENT_BOX ? borderAndPadding : 0)); if (style()->width().isPercent() || style()->height().isPercent() || style()->maxWidth().isPercent() || style()->maxHeight().isPercent() || style()->minWidth().isPercent() || style()->minHeight().isPercent()) m_minPrefWidth = 0; else m_minPrefWidth = m_maxPrefWidth; setPrefWidthsDirty(false); }
而 IE7(S) 则首先对比 IMG 元素的内在宽度和它的 'min-width'、'max-width',然后得出一个宽度的计算值, 再使用此计算值作为 'shrink-to-fit' 的首选最小宽度和首选宽度参与计算包含块的宽度计算。
在包含块的宽度计算需要依赖其内 IMG 元素时,尽量避免给 IMG 元素的 'min-width' 和 'max-width' 特性设置百分比单位的值。
操作系统版本: | Windows 7 Ultimate build 7600 |
---|---|
浏览器版本: |
IE7
IE8 Firefox 3.6.10 Chrome 7.0.524.0 dev Safari 5.0.2(7533.18.5) Opera 10.62 |
测试页面: | IMG_min-max-width_percent.html |
本文更新时间: | 2010-09-21 |
IMG min-width max-width percentage shrink-to-fit