为什么在Firefox中使用display: block; overflow: hidden包裹display: inline-block元素?

5

代码

#a {
  display: inline-block;
}
#b {
  float: left;
}
#c {
  display: block;
  overflow: hidden;
}
<span id="a">
    <span id="b">b</span>
    <span id="c">c1 c2</span>
</span>

在Chrome和IE中,它被渲染为:

Chrome

然而,Firefox 将其呈现为:

Firefox

您可以在此 jsFiddle中尝试。

问题

  1. 为什么会有这种差异?这是 Firefox 的 bug 吗?(如果是,我会很感兴趣得到链接,以便投票修复它。)
  2. 如何让 Firefox 像 Chrome 一样呈现它,考虑到下一部分提到的一些限制?

上下文

这里只是一点点背景知识,没有它,我上面尝试做的事情可能看起来有些疯狂。我打算将上述块用作标签,例如:

First name label

元素#a#b#c对应于:

What the elements correspond to

CSS旨在解决以下约束条件:
  1. 需要在IE8中工作(对我来说没有flexbox)。
  2. #a是可点击的,我不希望它向右扩展超过必要的范围,因此使用inline-block。但显然,另一种方法也可以实现相同的效果。
  3. 如果块#c的内容变得太长,它必须换行,不应该“下移”到块#b下面,如下所示。这就解释了overflow: hiddendisplay: table

Long label

2个回答

7

修复

不要混用,拥抱表格!

在Firefox(已测试31.2.0)和IE 8上“修复”它,并且更加语义化正确。

.a{ display:inline-table; max-width:45%; }
.b{ display:table-cell; padding-right:5px;}
.c{ display:table-cell; }
<span class="a">
  <span class="b">b</span>
  <span class="c">c1 c2</span>
</span>

<br /><hr /><br />

<span class="a">
  <span class="b">longer</span>
  <span class="c">Bacon ipsum dolor amet excepteur laboris irure, corned beef minim pastrami venison in anim incididunt strip steak ea non doner.</span>
</span>


问题原因

您原始代码的问题在于Firefox在计算收缩适应大小时完全将浮动元素排除在流程之外。虽然float会影响内联内容宽度,但它们本身不是内联的,因此仅从其余内容中占用水平空间-导致您看到的行为

以下是正在发生的演示(在Firefox中查看以实际查看)。请注意,.a.c上使用了outline,但.b上使用了厚重的border(实际上占用空间)。

.a { display: inline-block; outline: 1px solid blue; }
.b { float: left; border: 5px solid green; }
.c { display: table; outline: 1px solid red; }
<span class="a">
    <span class="b">b</span>
    <span class="c">c1 c2</span>
</span>
<br />
<span class="c">c1 c2</span>

这是设计上的问题,而非错误,并且查看当前规范, 基本框模型工作草案关于计算宽度的规范,你会发现浮动被标记为超出流,关于块格式化和内联格式化的规范明确说明float是内联上下文宽度的一部分,但不适用于块上下文(即inline-block内部)。因此,Firefox实际上是按照规范的严格解释行事-这在其他浏览器中技术上是一个错误。
然而,Firefox在处理此问题方面仍存在问题。
如果我们使浮动比我们的内部流内容更大(并且切换回边框作为轮廓包括溢出的子元素;我已将兄弟红色向左移动1像素,以便与其侄子克隆对齐)(再次在Firefox中查看)。

.a { display: inline-block; border: 1px solid blue; }
.b { float: left; border: 5px solid green; width:200px; }
.c { display: table; border: 1px solid red; }
.d { position:relative; left:1px; }
.e { max-width:100px; }
<span class="a">
    <span class="b">b</span>
    <span class="c">c1 c2</span>
</span>
<br />
<span class="c d">c1 c2</span>
<br />
^^^ without <em>.a</em> having a width limit
<hr />
vvv with <em>.a</em> having a width limit
<br />
<span class="a e">
    <span class="b">b</span>
    <span class="c">c1 c2</span>
</span>
<br />
<span class="c d">c1 c2</span>

蓝色的盒子被拉伸以容纳其整个绿色子元素,尽管根据规范的严格解释,它应该溢出。这是Firefox的一个错误,因为浮动只应影响内联上下文中父元素的width:auto。在块级上下文中,内部的inline-block应该是,浮动的宽度不包括在内。
请注意,这也是大多数其他浏览器的行为(在一定程度上- Firefox将使父元素大小等于float的框宽度,而其他浏览器将将流放在其旁边,如果父元素仍然可以增长),并且如果您提供任何width值或.a上的限制(如我的示例中的.e),则会得到预期的行为(如所示),即绿色浮动超出其父级(应该在所有浏览器中都是如此,因为所有这些行为问题都基于width:auto)。

致谢

感谢Oriol(另一个回答的作者)- 如果我没有和他争论这个问题,我就没有理由真正了解规格并弄清楚到底发生了什么。同时也要感谢他指出Firefox仍然会错误地呈现它-整个代码块都是多亏了他。


0

底层发生了什么

根据inline-block width#a的宽度是通过收缩适应算法计算的:
如果widthauto,则使用的值是收缩适应宽度。 收缩适应算法(现在称为适合内容的测量)是
min(max-content, max(min-content, fill-available))
这意味着,如果有足够的空间,其宽度将是内容的最大首选宽度(即,在显式换行之外不会断开行)。否则,它将与可用空间一样宽。
然后,浏览器首先计算#a的首选宽度。
通常,浮动重叠以下块框,如浮动所述:
由于浮动不在流中,因此在浮动框之前和之后创建的非定位块框垂直地流动,就像浮动不存在一样。
然后,Firefox似乎计算出了首选宽度,就好像#b#c可以重叠。因此,首选宽度是浮动#b的宽度和#c的宽度的最大值。
但是,#c由于overflow: hidden而建立了一个新的块格式化上下文(BFC):
具有“overflow”而不是“visible”的块框为其内容建立新的块格式化上下文。
在您的fiddle中,#c是一个表格,但是它是相同的,因为如表模型中所定义,
表包装器框架建立块格式化上下文。
但是,正如BFC next to floats中所解释的那样,BFC根不能重叠浮动:
表的边框框、块级替换元素或在正常流中建立新的块格式化上下文的元素(例如具有“overflow”而不是“visible”的元素)的边框框必须不与元素本身处于相同块格式化上下文中的任何浮动的边距框重叠。
相反,浮动可以将BFC向右“推”。这就是为什么在Chrome和Edge上,#a的首选宽度是#b#c的首选宽度之和的原因。
然后,如果它不大于可用宽度,#a的宽度将是在前一步中计算出的首选宽度。
否则,浏览器将通过在#b#c之间引入换行来计算首选最小宽度。因此,它将是#b#c的最小首选宽度的最大值。 #a的最终宽度将是该首选最小宽度和可用宽度之间的最大值。
现在已解决#a的宽度,因此也可以解决#b#c的宽度。
由于#b被浮动,因此它也将使用收缩适应算法进行调整大小。
如果#c是块,则将占用剩余空间(如果没有可用空间,则将其移动到下一行)。如果它是

摘要

  1. 默认情况下,块级元素会填充其包含块的可用宽度。
  2. 如果有一个比包含块小的浮动元素,后面的块级元素将重叠在它上面,并且仍然与包含块一样宽。
  3. 但是,如果该后续块级元素建立了 BFC,则它将缩小以避免与浮动元素重叠,并且只填充浮动元素剩余的空间。
  4. 默认情况下,浮动元素或内联块级元素会尝试与其内容所需的宽度一样宽。

Chrome 和 Edge 首先检测到 BFC 根不会与浮动元素重叠,然后内联块级父元素的宽度变为浮动元素和 BFC 根的宽度之和。

相反,Firefox 最初将 BFC 根视为普通块级元素,认为它会与浮动元素重叠,并相应地计算父元素的宽度。然后检测到它是 BFC 根,防止重叠并将其缩小以适应剩余空间。

因此,差异似乎在于它们按不同顺序计算父元素的宽度并防止 BFC 根与浮动元素重叠。

不正确的行为似乎是Firefox的问题。根据非替换块的固有尺寸所定义的,首选宽度应在布局后计算,而且很可能布局包括将BFC根推到浮动旁边。

块级容器框max-content measure是在布局后的尺寸,如果所有子元素都在max-size constraint下被定尺寸。

修复

#c上使用内联级别的显示方式,如display: inline-blockdisplay: inline-table,可以使Firefox在计算#a的宽度时将#b#c的宽度相加。这样,如果有足够的可用空间,#c就不会缩小,也不会出现换行。

问题在于,如果内联级别的内容太宽而无法适应浮动留下的剩余空间,则内联级别将移动到下一行而不是缩小。

.a { display: inline-block; }
.b { float: left;           }
.c { display: inline-table; }
<span class="a">
  <span class="b">b</span>
  <span class="c">c1 c2</span>
</span>
<hr />
<span class="a">
  <span class="b">longer</span>
  <span class="c">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut quis diam nec ligula iaculis lacinia non ac neque. Vivamus ut condimentum enim, et sollicitudin magna. In rhoncus nisi at est rhoncus feugiat sed vitae lacus. Maecenas sed felis et libero posuere iaculis a in lacus. Quisque eleifend auctor metus, a congue sapien venenatis a. Duis mollis mauris vitae massa mollis, nec porta nulla semper. Proin fringilla et nibh ac tempor. Aenean et augue ut dui pharetra scelerisque sed sit amet dolor. Nulla posuere a lorem sit amet vehicula. Morbi nec lacinia nibh. Suspendisse lacus nulla, dignissim et mi ut, luctus lobortis nisl. </span>
</span>

或者,如果旧浏览器的支持不是问题,您可以使用flexbox。一个简单的#a { display: inline-flex; }基本上可以工作,但以下代码更接近您当前的代码。

.a {
  display: inline-flex;
  align-items: flex-start;
  flex-wrap: wrap; /* Allow line breaks if #b is too wide */
}
.c {
  flex: 1; /* Initial width of 0, then take remaining space left by #b */
}
<span class="a">
  <span class="b">b</span>
  <span class="c">c1 c2</span>
</span>
<hr />
<span class="a">
  <span class="b">longer</span>
  <span class="c">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut quis diam nec ligula iaculis lacinia non ac neque. Vivamus ut condimentum enim, et sollicitudin magna. In rhoncus nisi at est rhoncus feugiat sed vitae lacus. Maecenas sed felis et libero posuere iaculis a in lacus. Quisque eleifend auctor metus, a congue sapien venenatis a. Duis mollis mauris vitae massa mollis, nec porta nulla semper. Proin fringilla et nibh ac tempor. Aenean et augue ut dui pharetra scelerisque sed sit amet dolor. Nulla posuere a lorem sit amet vehicula. Morbi nec lacinia nibh. Suspendisse lacus nulla, dignissim et mi ut, luctus lobortis nisl. </span>
</span>


我不确定你是否尝试过,但是 #c { display: block; overflow: hidden; }#c { display: table } 会产生相同的结果。我最初使用了 display: table,然后尝试了 display: block; overflow: hidden 作为创建BNF的替代方法。但在我的测试中,两者都产生了相同的结果。 - avernet
2
@abluejelly,CSS 不像优先考虑水平空间或垂直空间那样工作。这里的 #a 是根据收缩适应算法进行调整大小的,因此,如果有足够的空间(确实有),它应该具有所需的最大宽度,而不会在除显式换行符外的其他位置断开行。Firefox 似乎违反了这个规则。你说的 ipsum 是什么? - Oriol
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - avernet
@Oriol 我的理解虽然有很大偏差,但最终结果是正确的,哈哈。问题出在 float:left; 会导致初始大小从文档流中移除;请注意右边框线对齐:fiddle - abluejelly
@Oriol 在那个示例中完全忽略了宽度-你的边框不对齐的原因是它们是 border。将它们切换到 outline,它们就会对齐 fiddle。此外,我在规范文档 http://www.w3.org/TR/CSS2/visuren.html#floats 或者 https://drafts.csswg.org/css-box-3/#float 中没有看到任何暗示浮动宽度实际上是内联的,无论父级上下文是什么。一个 inline 对其内联内容进行 STF,float 是脱离文本流但影响文本流。Firefox 之所以“错误”,是因为其他浏览器做错了。 - abluejelly
显示剩余11条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接