打印

RD8004: IE6 IE7 IE8(Q) 中浮动元素宽度的 shrink-to-fit 算法与标准规定的算法不同

作者:孙东国

标准参考

如果一个浮动元素的 'width' 是 'auto',并且它是一个非替换元素,那么它的宽度将会是shrink-to-fit。

shrink-to-fit的计算公式:min(max(preferred minimum width, available width), preferred width)

CSS2.1 并未给出 preferred minimum width、available width 和 preferred width 确切算法,通常,将内容中非明确的换行外的其他部分强制不换行来计算 preferred width;反之,尝试将内容尽可能的换行,以得到 preferred minimum width;available width 即该元素的包含块的宽度减去 'margin-left','border-left-width','padding-left','padding-right','border-right-width','margin-right' 的值以及任何存在的纵向滚动条的宽度。

关于浮动非替换元素宽度计算的详细资料,请参考 CSS2.1 规范 10.3.5 Floating, non-replaced elements 中的内容。

问题描述

在 IE6 IE7 IE8(Q) 中,如果一个浮动元素的宽度是 shrink-to-fit,该元素的最终宽度可能比预期的小,并被尽可能的向上布局。

造成的影响

这个问题将导致一些元素在各浏览器中的实际尺寸不一致,在有些浏览器中可能发生布局混乱的现象。

受影响的浏览器

IE6 IE7 IE8(Q)

问题分析

分析以下代码:

<div id="Container" style="width:300px; background:lightgrey; font:14px Consolas; overflow:auto;">
    <div id="Placeholder_150" style="float:left; width:150px; height:30px; background:dimgray;"></div>
    <div id="Placeholder_100" style="clear:both; float:left; width:100px; height:30px; background:black;"></div>
    <div id="STF" style="float:left; background:url(x.gif);">
        <span id="A" style="background:darkgray;">background_darkgray</span>
        <span id="B" style="background:gray;">background_gray</span>
        <span id="C" style="background:dimgray;">background_dimgray</span>
    </div>
</div>
  • Container 是容器,设置宽度为 300px 的目的是限制 STF 的最大宽度。设置 'overflow:auto' 是为了使其创建 block formatting context 并自动容扩展高度容纳其内容,以便于查看其尺寸。设置了一个等宽字体(14px 的 Consolas 字体的每个英文字符的尺寸为 8px * 17px)的目的是为了便于计算 ABC 的宽度。
  • Placeholder_150 是一个 150px * 30px 的左浮动元素,事实上可以使用 'float:right' 来替代本例中的 'float:left'。放置这个元素的目的是用来占位,以更直观的测试 IE6 IE7 IE8(Q) 中 STF 宽度算法的问题。
  • Placeholder_100 是一个 100px * 30px 的左浮动元素,它也可以是一个右浮动元素。其作用与 Placeholder_150 相同。
  • STF 是一个没有设置width(默认值是auto)的左浮动元素,事实上可以使用 'float:right' 来替代本例中的 'float:left',关键点在于:它是一个宽度为 shrink-to-fit 的浮动元素。他被设置了一个网格状的图片背景,以便于查看其尺寸。
  • A 包含了 19 个不可换行的字符,尺寸为 152px * 17px。
  • B 包含了 15 个不可换行的字符,尺寸为 120px * 17px。
  • C 包含了 18 个不可换行的字符,尺寸为 144px * 17px。

注意:如果 STF 内包含右浮动元素,将触发由 IE6 IE7 IE8(Q) 中布局右浮动元素时的 Bug,导致 STF 的宽度尽可能的宽。

根据 CSS2.1 规范中 shrink-to-fit 的计算公式,获取 STF 的几个关键值:
preferred minimum width:将 STF 中所有元素尽可能的换行,最宽的元素是 B,为 152px。
available width:即 Container 的宽度,为 300px。
preferred width:将 STF 中所有元素尽可能的不换行,即 ABC 的宽度之和,再加上 AB 之间、BC 之间的两个压缩后的空格,为 432px。

将结果代入计算公式:
min(max(152, 300), 432) = 300

也就是说,最终 STF 的宽度应该是 300px。

这段代码在不同的浏览器环境中表现如下:

Snapshot

删除 Placeholder_100Placeholder_150 后:

Snapshot

可见,在 IE6 IE7 IE8(Q) 中,STF 的宽度计算并非是按照 CSS2.1 规范中描述的 shrink-to-fit 算法来进行的。

推测其算法如下:

  1. 尽可能的将浮动元素靠上布局在其包含块中,如果当前所在行的剩余横向空间小于其 preferred minimum width,则尝试下一行,以此类推,直到可以将其放置在这行为止。
  2. 然后,将其内容逐行顺次摆放,无法摆放时则折行继续摆放,直到其所有内容摆放完毕。
  3. 最后,取其内容中最宽的行的宽度为其最终宽度。

第一张图是因为第一行的 Placeholder_150 占据了 150px,导致该行剩余空间仅有 150px,无法放置 STF 中最宽的内容 A,因此换到第二行继续尝试。第二行的 Placeholder_100 占据了 100px,剩余 200px,大于 A 的宽度,因此在本行布局。顺次摆放其内容 ABC 后,取最长的一行 A 的宽度作为 STF 的最终宽度,即 152px。

第二张图的当前行宽度有 300px,因此 STF 在本行布局。放置了 AB 后,剩余空间不足以放置 C,因此折行继续。全部内容放置完毕后,将其内容中最宽的、放置了 AB 的行的宽度作为其最终宽度,即 152 + 120 + 8 = 280 (px)。(注:8px 是 AB 中间被压缩的空白字符的宽度。)

在其他浏览器中,第一张图中的 STF 以及第二张图中的 STF 的最终宽度均为 300px,是正确的值。

这个差异是由 IE6 IE7 IE8(Q) 中浮动元素的 shrink-to-fit 的不规范算法造成的。

该问题将导致受影响的元素的宽度比预期的小,并且尽可能的靠上布局。

该问题在 IE8(S) 中被修复。在 IE8(S) 中,浮动元素的 shrink-to-fit 的算法与 CSS2.1 规范中描述的算法一致。

如果一个页面在 IE6(S) IE7(S) 或 IE6(Q) IE7(Q) IE8(Q) 中被设计,并且有该问题存在,那么这个页面在其他浏览器中的布局将与预期的不符。

关于 block formatting context 的详细资料,请参考 CSS2.1 规范 9.4.1 Block formatting contexts 中的内容。

解决方案

这个问题的影响较大,避免该问题的最直接的方式是给浮动非替换元素指定一个宽度,而不使用其默认值 'auto',从而避免其宽度为 shrink-to-fit,以使页面布局在各浏览器中的表现一致。

参见

知识库

相关问题

测试环境

操作系统版本: Windows 7 Ultimate build 7600
浏览器版本: IE6
IE7
IE8
Firefox 3.6
Chrome 4.0.302.3 dev
Safari 4.0.4
测试页面: shrink_to_fit.html
本文更新时间: 2010-06-21

关键字

float left right shrink-to-fit 顶部 折行 缩小