本节摘要:语义化 HTML 指用标准定义的元素表达内容的含义,而不是用无语义容器加类名自己发明含义。它的价值在于:浏览器、读屏软件、搜索引擎、爬虫这些不吃 CSS 的程序,只能靠元素名理解页面。本节讲语义化的判据、标题层级与文档大纲、div 与 span 的合法席位,以及一份可执行的语义检查清单。
阅读完本节,你应当能够:
面试里"什么是语义化"的答案常常是一串元素名。真正的判据应该是一句话:换掉 CSS 和脚本之后,页面还剩多少信息量。把样式全剥掉、脚本全禁掉,浏览器、读屏、爬虫看到的就是那份裸 HTML——语义化衡量的正是这份裸文档的"自解释能力"。标题还在层级里、列表还是列表、引用还是引用,信息一分不丢;反之,全部是 div 与 span 的页面,剥掉样式后只剩一坨等价的文本块,谁先谁后、谁主谁次全靠猜。
为什么 class 命名补不上这个缺口?因为 class 的语义是页面作者与自己的约定,标准不知道、机器不认识。<div class="title"> 在机器眼里就是一个 div;<h1> 在机器眼里是一级标题——这个含义是规范写死的,全球所有消费者统一解读。这就是"私有命名"与"公共契约"的分野,也是语义化全部价值的源头。
语义不是写给 CSS 看的(CSS 对 div 和 article 一视同仁),它的消费者是三类程序:
浏览器本身。除了渲染,浏览器的辅助功能处处依赖元素语义:table 的表头关联、label 的点击聚焦、button 的键盘触发,全是标准行为。第 4 章会展开这些原生行为如何直接决定可访问性下限。
读屏软件。视障用户靠读屏听页面:听到"标题,二级,语义化 HTML"才知道走到了哪一层;听到"列表,四项"才知道这是并列信息。全是 div 的页面在读屏里是"没有结构的文字流",导航成本急剧上升。
搜索引擎与爬虫。搜索排序抓取的是裸 HTML,正文在 article 里、标题是 h1,这些信号直接影响页面被如何理解和呈现。结构化数据的富摘要(第 4 章 SEO 节)更是把"机器可读"变成了明码标价的收益。
语义化的重头戏是标题。h1 到 h6 不只是"字号从小到大",它们驱动着文档大纲——一份隐式的目录结构,读屏用户靠它跳转,搜索引擎靠它理解篇章。规则只有三条:每个页面一个 h1(主题唯一);层级不跳号(h2 后面接 h3,不直接跳 h5);不用标题元素制造字号(要大字用 CSS)。
<!-- 大纲正确 --> <h1>产品文档</h1> <h2>安装</h2> <h3>Windows 环境</h3> <h3>macOS 环境</h3> <h2>配置</h2> <h3>基础配置</h3> <!-- 大纲断裂:h2 直接跳到 h5,读屏跳转时结构错乱 --> <h1>产品文档</h1> <h2>安装</h2> <h5>Windows 环境</h5>
标题语义要与视觉解耦,这是新人最常见的倒挂:为了"字小一点"选 h4,为了"醒目"给侧栏广告上 h1。字号是 CSS 的职责,标题元素只回答"它在文档结构里是第几层"。
语义化不等于 div 有罪。标准对 div 的定义是"无通用语义的流容器"——它存在的意义就是在确实没有语义可表达时提供分组钩子。典型合法场景:纯为脚本挂载的容器、需要统一设样式的包裹层、布局用途的行与列。span 同理,是"无语义的行内容容器",给一段文字局部加样式时用它。
判别方法是反向的:每次写 div 之前问一句"这块内容有没有现成的语义元素"。是一段独立内容吗——article;是主题分组吗——section;是导航链接集吗——nav;是引文吗——blockquote;是强调吗——strong 或 em;只是要变个颜色吗——那才是 span。多数 div 是没问过这句话的产物。
<!-- 伪语义:机器只见两个 div --> <div class="nav"> <div class="nav-title">站内导航</div> </div> <!-- 语义化:机器精确理解每个节点 --> <nav aria-label="站内导航"> <h2 class="visually-small">站内导航</h2> </nav>
把高频纠结场景列成对照表,这张表可以直接当团队评审标准:
| 内容 | 正确元素 | 常见误用 |
|---|---|---|
| 页面主标题 | h1 | div 加大字号 |
| 段落 | p | br 连接的一整块 |
| 强调(语气) | em | i 或 span 加样式 |
| 重要(分量) | strong | b |
| 术语定义 | dfn / abbr | span |
| 引用块 | blockquote | div 加缩进 |
| 行内引文 | q | 手打引号 |
| 代码 | code | span 等宽字体 |
| 缩略语 | abbr 加 title | span |
| 图片及说明 | figure 与 figcaption | div 包 img |
| 联系信息 | address | div |
| 时间 | time | span |
em 与 strong 的分野值得单独说:em 是语气强调("我没说他偷懒",重音位置改变句义),strong 是重要性强调(警告语、关键词)。b 和 i 在 HTML5 里被重新定义为"习惯上用粗体斜体呈现但不带额外强调语义"的元素(如产品名、学名),日常工程里优先 em 与 strong 更少歧义。
figure 与 figcaption 是被低估的一对:任何"离开位置就看不懂、配了说明文字"的引用单元(图、代码清单、表格)都该装进去,语义上把作品与说明绑定为整体。
问:h1 到 h6 只用到前三级,后面的留着干什么?
多数页面确实只用到三级,深层级出现在长文档场景——法律条文、API 参考、课程大纲,第四五层在那里是常态。判断标准始终是"文档结构真实有几层",不是"我平时用几层"。
问:一个页面能有两个 h1 吗?HTML5 不是允许多 h1 了吗?
规范确实允许过(配合分段元素各起大纲),但后续实践发现读屏与搜索引擎对多 h1 的解析远不如预期一致,官方建议又回到了"每页一个 h1"的保守答案。工程决策跟着生态实际行为走,不跟规范的理论可能性走。
问:语义化影响性能吗?
基本不影响。元素名解析成本的差异可以忽略;真正的性能差异来自结构与样式、脚本的配合方式(选择器效率、重绘范围),那是第 3 章与第 5 章的话题。拿性能当不写语义的理由,属于方向找错了对象。
问:框架组件化之后,语义化还有意义吗?
更有意义了。组件把结构拆散到几百个文件里,"整页是什么结构"反而更容易失控;也正因为如此,每个组件内部的语义质量(列表是列表、按钮是按钮)直接决定拼出来的页面质量。框架解决的是工程组织问题,语义解决的是文档表达问题,两层互不替代。
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>语义对照实验</title> <style> .fake-title { font-size: 28px; font-weight: bold; } </style> </head> <body> <h1>真标题</h1> <div class="fake-title">假标题:字号一样大</div> <h2>一个水果列表</h2> <ul> <li>苹果</li><li>香蕉</li><li>樱桃</li> </ul> <div class="fake-list"> <div>苹果</div><div>香蕉</div><div>樱桃</div> </div> <p>下面这句话里有<em>语气重音</em>与<strong>重要提醒</strong>。</p> <blockquote> <p>标记的价值在于约定俗成。</p> </blockquote> </body> </html>
打开页面后做两件事:第一,看渲染结果——假标题与真标题肉眼几乎一样,机器层面天差地别;第二,禁用页面样式(多数浏览器开发者工具可做到),观察哪些结构仍然成立。这个实验把"语义给机器看、样式给人看"从口号变成亲眼所见。
讲价值讲完了,也要讲代价,不然这一节就成了布道文。语义化真实的成本有三块。学习成本:十几个语义元素的边界条件(article 与 section 的分界、aside 的双重含义)需要练习才能稳定掌握,团队里总有人处于学习曲线上。历史包袱:存量页面改语义结构不是查找替换,标题层级的调整可能牵动全站样式与脚本的选择器,改造成本随项目规模上涨。框架耦合:组件化框架的模板里,页面结构被拆散在几百个组件文件里,"整页语义"不再由单个文件决定,需要额外的约定(比如规定每页模板必须提供 main 与 h1)来保证。
对应地,我的落地建议是分场景定标准:新项目从第一行就按语义写,成本最低收益全程;存量项目按页面价值排优先级——流量入口页、内容页先改,后台管理界面后改甚至不改(它的读者少且固定,机器理解价值低)。有界投入换有界收益,不必把语义化当道德义务。
还有一个边界要说清:语义化与 ARIA 的关系。ARIA 属性(第 4 章详述)能补语义,比如给 div 标记 role。于是有人问:全用 div 再堆 ARIA 行不行?理论上行,实践上是灾难——ARIA 只改"声明",不改"行为",一个声明为按钮的 div 不会自动获得键盘支持,每个行为都得手补;而原生元素声明与行为是绑定的。所以工程铁律是"原生优先,ARIA 补漏",这条铁律同时也是对语义化价值的反向确认:元素语义是最便宜、最不容易写错的那一层。
问:语义元素上已有的样式(比如浏览器给 h1 的默认外边距)碍事怎么办?
用 CSS 重置或归一化样式表统一处理,这是第 3 章的标准动作。绝不要因此放弃语义元素改用 div——那是把"样式问题"错误地当成"结构问题"来治,方向反了。
对存量页面做语义改造时,我常用这份清单逐项过:
清单不必一次全过,改造存量页面时按"标题 → 分区 → 列表 → 细节"的顺序推进,收益最大的标题与分区先落地。
⚠️ 常见坑:为了语义化把所有 div 都换成 section。section 的定义是"主题分组,通常带标题"——没有标题的分组多半不需要 section,一个 div 就够。元素泛滥和元素缺席一样伤害可读性。
语义原理讲完,下一节把镜头拉到整页级别:七个分区元素如何拼出一份无歧义的页面骨架。