在Firefox中,内联块元素内的块级元素表现奇怪

5

有人能解释一下火狐浏览器中这种行为吗?

代码演示:http://jsfiddle.net/4mrt8wq3/

<style>
    .b { display: inline-block; }
    #a { display: block; }
</style>

<div class="b">
    <label>xxxxxxxxxx</label> 
    <input type="text" id="a"/>
</div>
<div class="b">
    <label>xxxxxxxxxx</label>
    <div> / </div>
</div>

只有在 Firefox 中,第一个 div 的位置比第二个 div 低一行。在 Chrome 和 IE(至少是 IE11)中,它的工作正常。就好像 inline-block 中的块元素由于某种原因在第二个元素下面换行了一样。
在第一个 div 上使用 overflow: hidden 可以解决问题,但第二个 div 的位置会略微奇怪,上面有大约 4 或 5 个像素的边距。在两个 div 上都放置 overflow-hidden 可以使其正确呈现。 我不是在寻找解决问题的方法,因为我已经找到了一个,但我无法解释这种行为…… 有人能解释一下为什么会这样吗?
2个回答

8
是的,有趣的问题。首先我们需要了解inline-block元素的默认垂直对齐方式是基线,并且每个这样的元素的基线是它们中最后一个行盒的基线。
在第二个class为“b”的div中,内部div本身包含一个行盒来容纳“/”字符。然后,该行盒为第二个class为“b”的div提供了基线。
该基线必须与class为“b”的第一个div的基线对齐。问题变成:那个div中最后一个行盒的基线在哪里?
通过使输入元素本身display:block,Firefox¹认为输入元素是“替换”的,其内容对CSS不透明,因此输入元素永远不会创建行盒。因此,class为“b”的第一个div的最后一行是包含标签的那一行,它与“/”字符的行水平对齐。
Chrome则持有不同的观点。Chrome将输入元素视为具有对CSS可见的内部结构,因此元素的内部形成一个行盒,其基线随后成为class为“b”的第一个div的基线,这就是与“/”字符水平对齐的基线。
当您添加`overflow:hidden'时,它会影响inline-block的基线,使它们的基线不再是其最后包含的行盒的基线,而是元素的底部边缘。
哪种行为是正确的还不清楚。这取决于历史和“替换”元素的概念有些混淆。在浏览器的早期版本中,某些元素的渲染被委托给外部系统,可以是底层操作系统或插件。特别是input元素就是如此,它的渲染是通过操作系统调用来完成的。操作系统没有CSS的概念,因此必须定义规则,以允许这些实际上是黑盒子的元素与页面的其余部分交互。这些元素被归类为“替换”元素。
请注意这个定义方式。如果浏览器选择将其渲染委托给CSS世界之外的系统,则元素是一个替换元素,没有官方的替换元素列表,因此理论上你可以有两个浏览器,一个委托元素的渲染,一个本地渲染它,并从CSS规则中获得非常不同的交互。
随着浏览器的发展,它们停止了将输入元素的渲染委托给其他系统并自己进行渲染,从而使渲染具有了CSS感知能力。这会引起一个问题,因为现有的网页假设输入元素将使用替换元素的规则进行渲染,这些网页可能会变得无法使用。如果一个浏览器允许这种情况发生,它将失去市场份额。因此,大多数浏览器实现这些元素的布局以与页面交互,就好像它们是替换元素一样,尽管在现实中它们并不是。
这方面的规范并不是很明确。HTML5规范没有将表单控件识别为替换元素,并建议将其呈现为内联块,这将使Chrome的行为正确,但包括Chrome在内的所有浏览器都有许多不同的行为方式。从与旧网页内容的向后兼容性角度来看,Firefox的行为更可靠。
在表单控件的布局得到比目前更紧密的规定之前,无法确定哪种行为是正确的。
¹对我而言,IE11的行为类似于Firefox。Opera 28(与Chrome相同的blink引擎)的行为类似于Chrome。Opera 12(presto引擎)的行为类似于Firefox。

这非常有趣。问题是,哪一个是正确的? - Erik Funkenbusch
那么你的意思是inline-block将整个内容视为单行,并具有单一基线? - Erik Funkenbusch
等一下...我不明白的部分是这个。带有/的div是块级元素,就像input一样。那么为什么一个是“不透明”的,而另一个不是呢?难道输入元素不是已经被替换了吗? - Erik Funkenbusch
输入元素可以被视为已替换(请参见我添加的正确性说明),但 div 元素绝对不会被替换。非替换块级元素(例如默认情况下的 div)具有堆叠线框的呈现方式。在示例中,'/'符号位于第一个(在这种情况下是唯一的)行框中。 - Alohci
就您特定的示例而言,是的,似乎是这种情况。请注意,浏览器可能会在同一元素上使用替换和非替换行为的混合方式,以实现足够的向后兼容性。 - Alohci
显示剩余2条评论

0

你的问题是,根据规范设置overflow:hidden会改变内联块的基线位置。Firefox实现了规范中的要求,而Chrome没有。

解决方案:

<style>
  .b { display: inline-block; 
  vertical-align: top; /*add this line */
  }
  #a { display: block; }
</style>

我不是在寻找解决问题的方案。 - j08691

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