不写 html 和 body 也能跑:解析器替你补了什么

有人给我看过一个 .html 文件,从头到尾只有几行:

<p>hello

存成 a.html,双击打开,正常显示。F12 看 Elements 面板,里面有完整的结构:

<html>
  <head></head>
  <body>
    <p>hello</p>
  </body>
</html>

这几层不是我写的,是 HTML 解析器在插入阶段补出来的。规范里明写了 html、head、body 的起止标签可以省略,解析器按固定规则补齐。所以”能不能省”这件事,答案是能,而且省了也不会出问题。

但另外两样省了会出问题,代价还不一样。

doctype:省掉就退回怪异模式

把 <!DOCTYPE html> 删掉,页面通常还是能看——直到你遇到某个宽度算不准、行高奇怪的地方。判断自己在哪个模式,一行就够:

document.compatMode

返回 "CSS1Compat" 是标准模式,返回 "BackCompat" 是怪异模式(quirks mode)。有 doctype 是前者,没有是后者。

怪异模式不是”兼容 IE6 的怀旧开关”,它会改一批盒模型相关的默认值。最直观的一条:怪异模式下 box-sizing 的初始值相当于 border-box,标准模式下是 content-box。也就是说同一个 width: 200px; padding: 10px,在两种模式下外框宽度一个 200、一个 220。

所以 doctype 这 15 个字符是整个文件里性价比最高的一行——它决定了后面所有尺寸计算的基准。

charset:位置比内容更容易出事

<meta charset="utf-8"> 的规范要求是:整个编码声明必须落在文档的前 1024 字节内。原因不复杂——浏览器开始解析时还没读完整个文件,它必须在某个点之前知道该用什么编码解码,否则只能先猜。

把 charset 挪到一坨注释后面,让它的字节位置超过 1024,会怎样?现代浏览器的编码嗅探相当聪明,中文环境里经常能猜回 UTF-8,于是你什么都看不到。这条规矩更接近”别去踩”,而不是”踩了必然报错”。真要验证,看这个:

document.characterSet   // "UTF-8"

它告诉你浏览器最终用了什么编码解码这个文档,比肉眼看有没有乱码可靠。

lang:影响字形,不是影响翻译

<html lang="zh-CN"> 最容易被当成 SEO 用的元数据。它真正的作用有几个,都和”渲染出来长什么样”直接相关:

  • 汉字字形选择。 中日韩共用大量码位,但字形不同——”直”字在日本字体里下部是竖直的,在中国字体里带一个小横。同一个 Unicode 码位,lang="ja" 和 lang="zh-CN" 可能渲染出两个字。
  • 断行规则。 不同语言允许断开的位置不一样,浏览器按 lang 选断行字典。
  • 读屏发音。 屏幕阅读器按 lang 切换语音库。整页写 lang="en" 的中文页面,读屏会用英文音库念中文,基本没法听。

最后这条是我改这个站的 lang 的原因。它不影响任何视觉,但决定了一个用读屏的人能不能听懂这个页面。

我的做法

骨架我现在还是全写。省下那几十个字节换不来任何东西,而多写一行 <html lang="zh-CN"> 能改变字形和读屏输出。能省 ≠ 值得省——这两件事在 HTML 里经常被混为一谈。

← 返回技术笔记