关于 DOCTYPE 的一个小发现

1,168 阅读4分钟
原文链接: zhuanlan.zhihu.com
本文来自我的笔记小札,找工作整理了出来
<!DOCTYPE html> 对于前端开发工程师(好吧,其实做 Web 都懂)而言,实在是太熟悉的一个东西了,由此引申的浏览器的各种模式也是一个老生常谈的坑。然而我在意外中竟然找到了一个鲜为人知的点,实在是惊讶!

# 问题起源

很久前知乎瞎逼逛的时候,发现有一个新的问题:发现html5一个很奇怪的问题...

使用 XHTML 声明时,图片是完美贴合包裹 DIV 元素的
但使用 HTML 5 标准声明时,包裹 DIV 元素底部会被撑开 2~5 个像素不等

考虑以下两段代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<body>
  <div style="background:black;float:left"><img src="http://photocdn.sohu.com/20110902/Img318184717.jpg" /></div>
</body>
</html>
<!DOCTYPE html>
<html>
<body>
  <div style="background:black;float:left"><img src="http://photocdn.sohu.com/20110902/Img318184717.jpg" /></div>
</body>
</html>

实际测试如下图所示,图片实际尺寸是(191 * 126)

&amp;amp;amp;amp;lt;img src="https://pic4.zhimg.com/v2-12c4edf3b80dd5e038370a4e2174296b_b.png" data-rawwidth="859" data-rawheight="465" class="origin_image zh-lightbox-thumb" width="859" data-original="https://pic4.zhimg.com/v2-12c4edf3b80dd5e038370a4e2174296b_r.png"&amp;amp;amp;amp;gt;

# 寻根问底

这就让人甚是疑惑,虽然实际开发中虽然一般重构基础过关的前端同学都知道,对于图片,一般都会使用 vertical-align: middle 使其与周边对齐,但是为什么使用老旧的文档声明反而是符合我们直观的显示呢?

本着兴趣和刨根问底的精神,我幸运地在国外程序员问答社区 **stackoverflow** 找到了类似的一个问答:html5 vertical spacing issue with image tag

首先,浏览器并没有所谓的“HTML 5 模式",而是只有三种:怪异模式(Quirks),几乎标准的模式(Limited Quirks)和标准模式(Standards),其中几乎标准的模式和标准模式之间的唯一差异在于是否对 <img> 元素给定行高(line-height)和基线(baseline)

几乎标准模式中,如果 <img> 标签所在行没有其他的行内元素,将不指定基线(baseline), <img> 标签因此会紧贴着父元素底部在标准模式中,行框总是会包含类似字母 g,f 尾巴下伸出来的那一部分空间(针对下行字母),即使行框内并没有任何内容。因此这种情况下你看到的 <img> 跟父元素底部几个像素的间隙实际上就是为”字母尾巴“预留的

使用 XHTML Transitional Doctype 会是浏览器运行在”几乎标准模式(Limited Quirks)”。如果你使用XHTML Strict 或者HTML 4 Strict模式,你将看到和使用HTML 5 模式同样的间隙,因为这是标准模式(Standards mode)

天哪,单单一个简单的 DOCTYPE 就要牵扯出奇怪的基线问题,而这个基线仅仅又是因为英文字母会有上行字母和下行字母的对齐问题

# 图片的最佳实践

知道了前面的缘由,我们就有理由在后续的开发中找到一条最佳实践的道路了,我思考了很久,给出一条建议是加上下列的 CSS Reset

img {
  max-width: 100%;
  max-height: 100%;
  vertical-align: middle;
}

虽然现在开发中已经不建议粗暴地加上 CSS Reset 而转为温柔的 normalize.css,但我有我的理由:

首先,绝大部分时候你不希望图片把包裹层撑破,因此前两条规则限制了无法无天的图片撑开

其次,由于前面的图片对齐问题,你可能需要在所有图片都手动对齐以防出现奇怪的几个像素

# 引申

实际上由这个问题我联想到了实际开发中,有时会偶尔遇到包裹元素莫名比里面的元素最大值多几个像素的 bug,直到现在我才恍然大悟!

图片本质上就是一个内联元素(或者说 inline-block,因为本身具有宽高),所以只要你在实际工作中,有若干个具备内联性质的元素水平对齐,就会出现这种情况:

<div class="wrap">
  这里是文字
  <div class="inline" style="display: inline-block; width: 100px; height: 100px;"></div>
</div>

你实际看到的 .wrap 在不同的浏览器中高度都是略大于 100px 的,因为它给隐形的 “尾巴” 留了空间(把 inline 当作图片啦),真是神奇呢!