What the hell is that 5px?

example-8-31.jpg

当 div 中包裹着一个 img 元素时,img 元素与父元素下边缘的 5px 间隙是怎么来的?

先揭晓答案:

在不同的文档模式中,当唯一的非表单控件类行内替换元素存在于其包容块中时,其父框的行高并不一定会计算文本基线高度。

先回顾一下答案中出现的几个概念,有助于更好的理解这个 5px 是怎么来的。

文档模式

目前浏览器的排版引擎有三种模式:

  • 怪异模式 Quirks mode
  • 接近标准模式 Almost standards mode
  • 标准模式 Standards mode

怪异模式是指一些网页浏览器为了维持对较旧的网页的向后兼容性,而使用的一种技术。它会尝试模拟更旧的浏览器(如IE)行为。它与其他模式之间一个突出的不同是对 CSS 盒模型的处理。在 IE6 之前,IE 浏览器使用的决定一个元素和模型宽高的算法与 W3C 规范所指定的算法相冲突。如下图所示,IE6 之前的浏览器中,盒模型的 content 部分包含了 border 和 padding.

标准模式下,浏览器渲染行为即 W3C 规范所描述的行为。

接近标准模式下,只有少数渲染方式与怪异模式相同(是否对行内可替换元素给定基线 baseline,在下文中会有提到),其他渲染方式与标准模式相同。

浏览器如何决定使用哪个模式?

通常,浏览器基于页面中 DOCTYPE 的存在以决定采用哪种渲染模式。为了确保你的页面使用标准模式,请确认你的页面如同本范例一样拥有 DOCTYPE:

<!DOCTYPE html>
<html>
  <head>
    <meta charset=UTF-8>
    <title>Hello World!</title>
  </head>
  <body>
  </body>
</html>

DOCTYPE 应被正确地放在 HTML 文件的顶端。任何放在 DOCTYPE 前面的东西,比如批注或 XML 声明,会令 Internet Explorer 9 或更早期的浏览器触发怪异模式。

此外,一个不含任何 DOCTYPE 的网页将会以怪异模式渲染。

而出发接近标准模式的 DOCTYPE 是这样的:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

在 HTML5 中,DOCTYPE 唯一的作用是启用标准模式。而在更早期的 HTML 标准中则会有其他意义。

可替换元素

From the CSS2.1 spec:

Replaced ElementAn element whose content is outside the scope of the CSS formatting model, such as an image, embedded document, or applet. For example, the content of the HTML IMG element is often replaced by the image that its "src" attribute designates. Replaced elements often have intrinsic dimensions: an intrinsic width, an intrinsic height, and an intrinsic ratio.

根据 CSS spec 所描述,可替换元素的样式模型不在一般的 CSS 格式化模型范围内。这些元素在被替换之前往往没有实际内容。

常见的可替换元素有:

  • img
  • object
  • video
  • textarea
  • input

这里要注意的是,在接近标准模式下,非表单类的行内替换元素如 img 的一个特点是: 行内替换元素本身不存在基线位置。

那么什么是基线?

基线 baseline

基线是排版上的一种名词,是指大部分字母所“坐落”其上的一条看不见的线,每条基线之间形成基本的基线网格。

baseline

垂直对齐样式 veriticle-align

此样式负责影响垂直位置上行内框在行框中产生的位置,它的默认值为 baseline。此时行内框的基线对齐父框的基线。如果该行内框没有基线,将框的底线对齐父框的基线。

行高样式 line-height

行高所指定的高度用于行框的高度计算,但需除去行内替换元素,其高度设定来自于 height 属性设置 。它的默认值为 normal,表明将基于字体尺寸计算出一个合理的值。

回到问题所在

回顾完以上概念后,现在回到实际问题:当 div 中包裹着一个 img 元素时,img 元素与父元素下边缘的 5px 间隙是怎么来的?

<div>
  <img src="https://is1-ssl.mzstatic.com/image/thumb/Features118/v4/15/92/2c/15922c11-d022-f4d1-d695-c2adfb2fa831/source/836x600bb.jpg" alt="pic">
</div>

父元素 div 没有设置 height,其 line-height 值为 normal, 整个高度是由 img 支撑起来的。这 5px 间隙,是在 chrome 标准模式下产生的。

而标准模式与接近标准模式的差异在于:

  • 在接近标准模式下,若 img 标签所在行没有其他的行内元素,将不指定基线 baseline,当 veriticle-align 的值为 baseline 时,img 会紧贴着父元素底部。
  • 在标准模式下,行框总是会包含类似字母"g","f"尾巴下伸出来的那一部分空间(针对下行字母),即使行框内并没有任何内容。这就是所谓的「基于字体尺寸计算出一个合理的值」。

解决方案

已经得出结论:在标准模式下,上述 DOM 组合中的父元素高度是计算错误的,那么 img 与父元素的对齐就会造成间隙。若需要行内替换元素与其父容器底部无间隙,修改 vertical-align 值为非 baseline 即可。


参考资料

Show Comments