“语义化”这三个字,在前端圈被念叨了十几年,但真正把标签用对的项目依然是少数。随手打开一个线上站点,你大概率还是会看到成片的 <div class="header">、<div class="nav">、<div class="main">——用 class 名字把标签的语义“喊”出来,却在结构上什么都没告诉浏览器。HTML5 在 2014 年成为 W3C 正式推荐标准,为 Web 带来了一整套表达结构的原生标签。它们不改变一个像素的视觉效果,却决定了屏幕阅读器能否读懂你的页面、搜索引擎能否理解你的内容、半年后的你能否快速找回那段代码。本篇 20 分钟长文,从“什么是语义化”讲起,逐一拆解结构标签、文本级标签、表单、媒体、交互元素与文档大纲,并给出可落地的重构方法与检查清单。
一、什么是语义化,以及它到底值多少钱
语义化(Semantic HTML)的核心只有一句话:用最能表达内容含义的标签,去承载这段内容。标题就用 h1,不要用 <div class="title">;导航就用 nav,不要用 <div class="nav">;一段强调就用 strong,不要用 <span style="font-weight:bold">。
这不是审美偏好,而是因为你的 HTML 文档同时有三类“读者”,而它们都看不见你的 CSS:
从 div 汤到结构化文档
在 HTML4 时代,页面结构只能靠 id 和 class 表达。浏览器知道那是一个 div,但不知道它是导航还是正文;屏幕阅读器只能顺序朗读,无法提供“跳到主内容”这类快捷操作;爬虫要判断哪块是正文,只能靠一堆启发式规则去猜。
HTML5 的做法是:把这套“约定俗成”的命名模式固化成真正的元素。于是 header、nav、main、article、section、aside、footer、figure、time、mark 等一批标签进入了标准。它们默认的 CSS 表现与 div 几乎完全一致(多为 display: block),但携带了浏览器与辅助技术可以直接消费的语义信息。
语义 ≠ 样式,这是最容易混淆的一点
很多开发者抗拒语义化标签的理由是:“反正我都要写 CSS,用 div 也一样”。这句话混淆了两个层次:
- 语义层回答“这是什么”——一个独立的文章、一组导航、一段被强调的文字。
- 表现层回答“它长什么样”——字号、颜色、间距、圆角。
HTML 负责前者,CSS 负责后者。用 h1 不意味着它必须很大很粗——你完全可以用 CSS 把它做成 14px 的小字。反过来,把大字号写在 div 上,也不会让它变成一个标题。标签选择应该由内容语义决定,而不是由视觉效果倒推。
div 更合适。”——这是在用视觉反推结构,屏幕阅读器会完全读不到这个标题。
h2,视觉上用 CSS 覆盖:font-size: 0.95rem;。语义与样式各司其职。
二、文档结构标签:搭好页面的骨架
结构标签是 HTML5 语义化最重要的部分。它们共同构成了页面的“地标”(Landmark),让辅助技术用户可以像使用 App 底部导航一样,在页面各大区域之间快速跳转。点击下面的每个标签,看看它承担什么职责。
一张骨架示例
把上面这些标签按真实页面组合起来,结构大致如下:
<header>
<a class="logo">前端开发技巧</a>
<nav>
<ul>
<li><a href="/">首页</a></li>
<li><a href="/css">CSS</a></li>
</ul>
</nav>
</header>
<main>
<article>
<header>
<h1>高性能 CSS 动画技巧</h1>
<p><time datetime="2026-09-18">2026年9月18日</time></p>
</header>
<section>
<h2>渲染流水线</h2>
<p>……</p>
</section>
<aside>
<h2>相关阅读</h2>
</aside>
<footer>
<p>© 2026 前端开发技巧</p>
</footer>
</article>
</main>
<footer>
<p>备案信息……</p>
</footer>
</body>
容易踩坑的四条规则
main只能有一个:HTML 规范要求一个文档中只能有一个非hidden的main,并且它不能是article、aside、footer、header、nav的后代。header/footer不限于页面级:写在article里就是这篇文章的页眉页脚,可以出现多次。很多开发者误以为它们只能出现一次。section必须有标题:如果没有合适的标题,说明它不是一个“主题分组”,请退回使用div。nav要克制:不是所有链接集合都叫导航。把页脚的一堆链接、文章里的“相关阅读”都塞进nav,反而会让地标列表变得嘈杂。
div 与 span 依然不可替代
语义化不等于“消灭 div”。div 的定位是无语义的通用容器,用于分组、布局、挂载样式或脚本钩子。当你需要一层纯粹为了 flex 布局的包裹元素时,div 就是最正确的选择——给它套上一个 section 反而是在撒谎。
| 标签 | 语义 | 典型场景 | 常见误用 |
|---|---|---|---|
header |
介绍性内容 | 站点页头、文章标题区 | 当成“页面顶部固定条”的样式容器 |
nav |
主要导航 | 主导航、面包屑、分页 | 把所有链接列表都包成 nav |
main |
页面主内容 | 每页唯一一次 | 嵌套在 article 内部 |
article |
可独立分发 | 文章、评论、卡片 | 把整页内容包一层当成“大容器” |
section |
主题分组 | 带标题的章节 | 纯样式包裹、没有标题 |
aside |
附属内容 | 侧边栏、引用框、广告 | 把主要内容放进去 |
footer |
结尾信息 | 版权、备案、作者署名 | 里面塞主内容导航 |
div |
无语义容器 | 布局、样式钩子 | 本该用语义标签时一律用它 |
三、文本级语义:让每个词都有出处
结构标签决定页面的大块划分,文本级标签则决定句子内部每个片段的性质。它们大多是行内元素,视觉上与 span 无异,但语义差别巨大——尤其是对屏幕阅读器而言,strong 会被改变语调,em 会被加重读,abbr 可以被展开。
强调类:strong 与 em
这两个标签最容易被误用。关键在于区分“重要性”和“重音”:
strong表示重要性、严重性、紧急性。屏幕阅读器通常会加重语气。b只是视觉加粗,没有强调语义,用于产品名、关键词高亮等场景。em表示重音强调,会改变句子的意思。比如“我没说你偷了钱”和“我没说你偷了钱”,重音不同,含义完全不同。i则用于外语词、科技术语、心语等,不表示强调。
经验法则:如果删掉这个标签,句子含义不变,那它就不该用 strong/em,用 b/i 或者干脆不用。
引用与来源:blockquote、q、cite
<p>语义化不是让浏览器看得懂,而是让所有读者都看得懂。</p>
<footer>—— <cite>某位前端工程师</cite></footer>
</blockquote>
<!-- 短引用用 q,浏览器会自动加引号 -->
<p>他常说 <q>先写结构,再想样式</q>。</p>
<!-- cite 用于作品名,不是人名! -->
<p>推荐阅读 <cite>HTML 标准</cite>。</p>
时间与缩写:time 与 abbr
time 是 HTML5 中性价比极高的一个标签。它的 datetime 属性让机器可以精确解析时间,而页面显示可以是任意人读格式:
<time datetime="2026-09-18T14:30">今天下午两点半</time>
<time datetime="PT2H30M">两个半小时</time>
<abbr title="Cascading Style Sheets">CSS</abbr>
<abbr title="HyperText Markup Language">HTML</abbr>
搜索引擎会利用 datetime 判断内容时效性,社交平台抓取摘要时也会优先读取它。这是少数“只写一行代码就能带来实际 SEO 收益”的标签。
代码与键盘:code、pre、kbd、samp、var
<!-- pre 保留空白与换行,常与 code 嵌套 -->
<pre><code>
const sum = (a, b) => a + b;
</code></pre>
<!-- kbd:用户按下的键 -->
<p>按 <kbd>Ctrl</kbd> + <kbd>S</kbd> 保存。</p>
<!-- samp:程序输出 -->
<p><samp>Build succeeded.</samp></p>
<!-- var:变量或占位符 -->
<p>函数 <var>fn</var> 接收参数 <var>x</var>。</p>
其余高频文本标签速查
mark:与当前上下文相关的高亮,例如搜索结果命中词。它自带黄底,比用span+ 背景色更有语义。del/ins:文档修订中的删除与插入,配合datetime与cite可记录修改时间与原因。s:表示内容已不再准确或不再相关,例如原价划掉。注意与del的区别——s不是修订痕迹。small:附属细则、法律声明、版权信息。它不是“小字号的通用容器”。sub/sup:下标与上标,如 H2O、x2。如果只是为了排版错位,不要用它们。dfn:术语的首次定义处。常与abbr搭配使用。address:最近的article或body的联系信息,通常是邮箱、电话、地址,不要用它包裹任意地址文本。bdi/bdo:处理双向文本(如混排阿拉伯语、希伯来语)时的方向隔离与覆盖。ruby/rt/rp:东亚文字的注音标注,如振假名。wbr:可换行断点,告诉浏览器“实在放不下时可以在这里断行”,常用于长 URL。
<span style="font-weight:bold">警告:</span>此操作不可撤销。屏幕阅读器读起来毫无波澜,用户完全感受不到“警告”的分量。
<strong>警告:</strong>此操作不可撤销。语义化的强调,多数屏幕阅读器会改变语调或插入提示。
四、列表、表格与表单:结构化数据的三个重灾区
列表:ul、ol、dl 各有分工
列表是导航菜单、文章目录、步骤说明的标准结构。除了 ul 与 ol,HTML5 还保留了一个常被忽略的 dl(描述列表):
<ul>
<li>语义化</li>
<li>无障碍</li>
</ul>
<!-- 有序列表:顺序有意义,可用 start / reversed -->
<ol start="3" reversed>
<li>第三步</li>
<li>第二步</li>
</ol>
<!-- 描述列表:术语与解释成对出现 -->
<dl>
<dt>语义化</dt>
<dd>用恰当的标签表达内容的含义与结构。</dd>
<dt>无障碍</dt>
<dd>让所有人,包括使用辅助技术的用户,都能平等使用产品。</dd>
</dl>
表格:不要用 table 做布局,但数据一定要用 table
过去十年“不要用 table 布局”的呼吁,让不少开发者走向另一个极端:连数据表格也用 div 拼。这是错的。数据表格用 div 搭建后,屏幕阅读器无法朗读“第 3 行第 2 列”,用户彻底失去行列关系。
一个无障碍友好的数据表格,应该包含以下要素:
caption:表格标题,屏幕阅读器进入表格时会优先朗读它。thead/tbody/tfoot:区分表头、主体、表尾,让长表格滚动时表头可保持。th+scope="col"或scope="row":明确表头作用范围。- 复杂表格用
id+headers建立单元格与表头的显式关联。
<caption>2026 年第二季度各浏览器市场份额</caption>
<thead>
<tr>
<th scope="col">浏览器</th>
<th scope="col">份额</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Chrome</th>
<td>65.4%</td>
</tr>
</tbody>
</table>
表单:语义化的收益最直接
表单是用户与页面交互最密集的地方,也是语义化收益最明显的地方。一个语义正确的表单,能自动获得:点击标签聚焦输入框、屏幕阅读器正确朗读、浏览器自动填充、移动端弹出合适的键盘类型。
<fieldset>
<legend>订阅资讯</legend>
<!-- label 的 for 与 input 的 id 一一对应 -->
<label for="email">邮箱地址</label>
<input
type="email"
id="email"
name="email"
autocomplete="email"
required
aria-describedby="email-hint">
<p id="email-hint">我们不会向第三方分享你的邮箱。</p>
<button type="submit">订阅</button>
</fieldset>
</form>
一个高频错误是用 placeholder 充当标签。placeholder 在用户开始输入后就消失了,用户无法回头确认这个字段到底要填什么;屏幕阅读器对它的支持也参差不齐。正确做法是始终提供可见的 label,placeholder 只作为格式示例。
另一个细节是 button 的 type 属性。在 form 内部,不写 type 的 button 默认是 submit,很容易导致“点了个普通按钮却把表单提交了”。任何非提交按钮都请显式写上 type="button"。
五、媒体与嵌入:图、音、视的语义表达
figure 与 figcaption:让配图有说明
正文中的插图、代码清单、图表,往往需要一段说明文字。过去只能用 div 包一层 img 再加一个 p,语义上是一片空白。figure 与 figcaption 正是为此而生:
<img src="chart.png" alt="2026 年各季度访问量趋势图">
<figcaption>图 1:访问量在第三季度出现明显增长</figcaption>
</figure>
<!-- figure 里也可以放代码清单 -->
<figure>
<pre><code>npm run build</code></pre>
<figcaption>清单 1:构建命令</figcaption>
</figure>
figcaption 必须是 figure 的第一个或最后一个子元素,位置决定它显示在媒体上方还是下方。
picture 与 source:响应式图片的语义方案
很多开发者只知道 srcset,却忽略了 picture。两者的分工是:
srcset+sizes:同一张图的不同分辨率,由浏览器根据 DPR 与视口宽度自行选择。picture+source:艺术指导场景,不同条件用不同构图或格式,例如桌面端用横图、移动端用竖图,或优先使用 AVIF/WebP。
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<source
media="(max-width: 600px)"
srcset="hero-mobile.jpg">
<img
src="hero.jpg"
alt="团队成员在会议室讨论方案"
width="1200"
height="600"
loading="lazy">
</picture>
注意最后那个 img:它是必需的,承担实际的渲染、alt 文本与尺寸占位。同时写上 width 与 height 可以避免图片加载完成后的布局偏移(CLS),这也是语义化之外一个常被忽视的性能细节。
video、audio 与 track
媒体元素的原生语义能力包括:controls 提供无障碍控件,poster 指定封面,track 提供字幕与说明:
<source src="intro.webm" type="video/webm">
<source src="intro.mp4" type="video/mp4">
<!-- 字幕:听力障碍用户的必需品 -->
<track kind="captions" src="zh.vtt" srclang="zh" label="中文" default>
<track kind="descriptions" src="desc.vtt" srclang="zh">
<!-- 兜底内容:浏览器不支持时显示 -->
您的浏览器不支持视频播放,<a href="intro.mp4">点击下载</a>。
</video>
track 的 kind 有多种取值:captions(含音效描述的字幕)、subtitles(翻译字幕)、descriptions(画面描述,供视障用户)、chapters(章节导航)、metadata(元数据)。只要视频里有说话,就应该提供 captions。
iframe 与 canvas 的语义补充
iframe必须写title属性,否则屏幕阅读器只能读成“框架”,用户不知道里面是什么。嵌入第三方内容时,配合loading="lazy"与sandbox使用。canvas本身没有任何语义,里面的内容对辅助技术完全不可见。必须在标签内部提供回退内容,或使用role="img"+aria-label描述图形含义。svg内联到页面时,装饰性图标应加aria-hidden="true"与focusable="false";有含义的图标则加role="img"与<title>。
六、原生交互元素:不写 JS 也能用
HTML5 引入了一批原生交互元素,它们自带键盘支持、焦点管理和无障碍语义。能用原生元素的场合,永远不要用 div + JS 手搓。
details 与 summary:原生手风琴
<summary>语义化真的能提升 SEO 吗?</summary>
<p>会。搜索引擎会解析文档结构,语义清晰的页面更容易被正确理解与提取摘要。</p>
</details>
<!-- 用 name 属性让多个 details 互斥(手风琴效果) -->
<details name="faq">…</details>
<details name="faq">…</details>
details 天生支持键盘操作(Tab 聚焦、Enter/Space 展开)、支持屏幕阅读器朗读“展开/折叠”状态,还自带 toggle 事件。open 属性可以控制默认展开。它是 FAQ、折叠面板的最优解。
dialog:原生模态框
自己实现的模态框,最容易出问题的地方在于焦点管理:打开时焦点没有移入、关闭时没有归还、Tab 能跑到背后的内容上、屏幕阅读器还能读到后面的内容。dialog 把这些全部内置了:
<h2>确认删除?</h2>
<p>删除后无法恢复。</p>
<form method="dialog">
<button value="cancel">取消</button>
<button value="ok">删除</button>
</form>
</dialog>
// 模态打开:自动聚焦、自动锁定背景、Esc 关闭
dialog.showModal();
// 非模态(更像 popover)
dialog.show();
配合 ::backdrop 伪元素可以自定义遮罩层样式,配合 closedby 属性还能控制点击遮罩是否关闭。
progress 与 meter
progress:表示任务的完成进度。value与max都存在时是确定进度,只写max或都不写则是“不确定进度条”(转圈动画)。meter:表示某个已知范围内的标量值,例如磁盘占用、评分、温度。low/high/optimum可以让浏览器自动渲染出不同的颜色语义。
<progress id="upload" value="70" max="100">70%</progress>
<label for="disk">磁盘占用</label>
<meter id="disk" value="0.82" min="0" max="1"
low="0.3" high="0.8" optimum="0.2">82%</meter>
template 与 slot:Web Components 的基石
template 里的内容在页面加载时不会被渲染、不会执行脚本、不会加载图片,直到被 JS 克隆出来才“生效”。它是声明式渲染与组件化的基础:
<article class="card">
<h3><slot name="title"></slot></h3>
<p><slot></slot></p>
</article>
</template>
<my-card>
<span slot="title">语义化标签</span>
用原生元素表达结构含义。
</my-card>
七、文档大纲与标题层级
HTML5 早期曾设计过一套“大纲算法”(Outline Algorithm),允许每个 section 拥有自己的 h1 并由浏览器计算出层级。但这套算法从未被任何浏览器或屏幕阅读器真正实现,如今已被规范移除。所以,现实世界的规则回到了最朴素的一条:
用 h1~h6 的层级数字,手工表达标题的嵌套深度,不要跳级。
屏幕阅读器的“按标题浏览”
主流屏幕阅读器(NVDA、JAWS、VoiceOver)都提供“按标题跳转”的快捷键。用户打开一个长页面,第一件事往往就是拉出标题列表,快速判断这个页面有没有自己要找的内容。
如果你的标题层级混乱——比如满屏 h3、或者所有标题都是 <div class="title">——这份列表就是空的,用户只能一个字一个字地听下去。这不是“体验差一点”,而是直接把人挡在门外。
视觉层级与语义层级分离
常见的两难是:设计稿上某个标题看起来不大,用 h2 会导致字号过大。解决办法永远是 CSS:
.sidebar h2 {
font-size: 0.95rem;
font-weight: 600;
letter-spacing: 0.02em;
text-transform: uppercase;
}
/* 只在视觉上移除标题,但保留给屏幕阅读器 */
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
}
/* 千万不要用 display: none 隐藏标题——它也会被辅助技术忽略 */
这个 .visually-hidden 技巧用途很广:给没有可见标题的区块(例如侧边栏的相关推荐列表)补一个标题,或者给“跳转到主内容”的链接提供文本。
跳转到主内容:一个只需三行代码的优化
键盘用户在每个页面都要先 Tab 过整个导航栏才能到达正文。一个“跳到主内容”的链接可以省掉这几十次按键:
<main id="main" tabindex="-1">……</main>
/* 平时隐藏在视口外,获得焦点时滑入 */
.skip-link {
position: absolute;
left: -9999px;
z-index: 999;
}
.skip-link:focus {
left: 10px;
top: 10px;
background: #2563eb;
color: white;
padding: 8px 16px;
}
八、无障碍:语义化最大的受益者
如果说语义化只对一件事产生决定性影响,那一定是无障碍。屏幕阅读器用户浏览网页的主要方式有三种,每一种都建立在语义之上:
- 按地标跳转:在 header、nav、main、aside、footer 之间快速切换,跳过不关心的区域。
- 按标题跳转:拉出标题列表,快速定位到目标章节。
- 按元素类型跳转:直接跳转到下一个链接、按钮、表单控件或列表。
这三种方式全部依赖正确的标签。一个用 div 拼出来的页面,这三种导航方式全部失效。
原生语义 > ARIA
ARIA(Accessible Rich Internet Applications)提供了一套属性,可以给无语义的元素“补充”语义。但 WAI-ARIA 规范的第一条规则就是:
如果存在一个原生的 HTML 元素或属性能够表达所需的语义和行为,就使用它,而不是重新定义一个。
原因很简单:原生元素自带键盘交互、焦点管理、状态同步和跨浏览器兼容,而 role 只是给辅助技术“贴了个标签”,行为还得你自己实现。比如:
<div role="button" tabindex="0" onclick="...">提交</div>需要自己处理 Enter/Space 键、禁用状态、焦点环,还容易被辅助技术误报。
<button type="button">提交</button>键盘、焦点、屏幕阅读器、表单行为全部原生支持,零额外代码。
五个零成本的无障碍细节
- 图片写 alt:有含义的图写描述,纯装饰的图写
alt=""(空字符串,而非省略)。省略 alt 会让屏幕阅读器朗读文件名。 - 链接文本要有意义:避免满屏“点击这里”。屏幕阅读器用户常常会拉出链接列表,一堆“点击这里”毫无信息量。用
aria-label或视觉隐藏文本补充上下文。 - 表单控件必须有标签:
label是标准方案,aria-label是退路,placeholder 不能替代。 - 不要用颜色单独传递信息:错误提示只标红是不够的,要配上图标或文字。色盲用户看不出差别。
- 保持焦点可见:不要写
outline: none而不给替代样式。键盘用户靠焦点环判断自己在哪里。
用辅助技术自测
不需要安装专业软件,你就能做一次基础检查:
- 拔掉鼠标,只用 Tab / Shift+Tab / Enter / Space 走一遍页面,看能否完成所有操作。
- 打开 macOS 的 VoiceOver(Cmd+F5)或 Windows 的 Narrator,听听它读出来的地标和标题列表是否合理。
- 在 DevTools 的 Elements 面板中查看 Accessibility 侧栏,可以看到浏览器为每个元素计算出的可访问名称与角色。
- 用 Lighthouse 的 Accessibility 审计跑一遍,重点关注“元素是否使用了正确的角色”这类提示。
九、SEO 与机器可读:语义化的额外红利
语义化不是 SEO 的银弹,但它确实能为搜索引擎提供更准确的信号。搜索引擎爬虫在解析页面时,会利用标签语义来判断内容的层级与重要性。
爬虫如何理解你的页面
- main:帮助算法区分正文与页头页脚导航,减少“正文提取”环节的噪声。这对内容型站点尤其重要。
- article:标明这是一个可独立分发的内容单元,有助于识别内容边界。
- h1~h6:构成内容大纲,帮助算法理解主题与子主题的关系,也常用于生成页面摘要。
- time + datetime:提供机器可读的发布时间,影响时效性排序与“最新内容”的判定。
- figure / figcaption:说明图片与正文的关联,图片搜索时会参考这段文字。
- a 的 rel 属性:
rel="nofollow"、rel="sponsored"、rel="ugc"向爬虫声明链接的性质。
结构化数据:在语义化之上的增强
语义化标签解决的是“页面结构”,而 JSON-LD 结构化数据解决的是“实体关系”。两者配合,可以让搜索结果中出现富媒体摘要(评分、价格、FAQ 折叠、面包屑):
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "HTML5语义化标签与应用",
"datePublished": "2026-09-18",
"author": {
"@type": "Person",
"name": "张玥"
}
}
</script>
注意:结构化数据必须与页面可见内容一致。用 JSON-LD 声明了一条并不存在的 FAQ,属于违反搜索引擎指南的行为,可能导致手动惩罚。
面向 AI 时代的可读性
随着大模型成为新的“内容消费者”,语义化的价值又多了一层。当检索系统或 AI 爬虫需要从页面中提取正文时,它们同样依赖结构信号:
- 清晰的
main与article边界,能让提取算法准确圈定正文范围。 - 正确的标题层级,能让模型理解内容的分段与从属关系,生成更准确的摘要。
- 语义化的列表与表格,能让结构信息在纯文本转换后依然保留。
- 合理的
header/footer划分,能减少导航噪声对内容提取的干扰。
换句话说,你今天写下的每一个语义标签,都在为未来十年的机器读者铺路。
十、八个最常见的语义化误区
section 当成“比 div 更高级的 div”,见块就套,且从不给标题
h1,或者标题从 h1 直接跳到 h4
role="button" + div 替代原生 button
nav 用在页脚的一堆零散链接上
<br> 制造段落间距,用 <div> 充当段落
placeholder 当标签,或用 title 属性传关键信息
h1 和关键词,导致结构失真
两个需要展开说的点
关于 <br>:它只能用于真正的换行语义,例如诗歌、地址、歌词。用它来制造段落间距,屏幕阅读器不会读出停顿,搜索引擎也看不到段落划分。段与段之间应该用 p,间距交给 CSS 的 margin。
关于 title 属性:浏览器对 title 的呈现方式(悬停提示)在触屏设备上完全不可用,键盘用户也难以触发,屏幕阅读器的支持程度参差不齐。因此 title 只能作为补充,关键信息必须写在可见文本里。abbr 的 title 是个例外,因为它本身就是解释性内容。
十一、重构实战:把 div 汤改造成语义结构
下面是一段典型的“div 汤”页面骨架。它用 class 名字描述了所有语义,但在 HTML 层面什么都没说。点击按钮对比改造前后的结构。
<div class="top">
<div class="logo">前端开发技巧</div>
<div class="menu">
<div class="menu-item"><a href="/">首页</a></div>
<div class="menu-item"><a href="/css">CSS</a></div>
</div>
</div>
<div class="content">
<div class="post">
<div class="post-title">语义化标签</div>
<div class="post-date">2026-09-18</div>
<div class="post-body">正文内容……</div>
</div>
<div class="side">作者简介</div>
</div>
<div class="bottom">© 2026</div>
</div>
改造带来的六个具体收益
- 屏幕阅读器:可以按地标跳转,一键直达 main 内容;导航被朗读为“导航,包含 2 个项目”。
- 阅读模式:浏览器的 Reader Mode 能准确提取正文,不再把侧边栏和页脚一起抓进去。
- 搜索引擎:正文边界清晰,摘要提取更准确;
time提供了可靠的发布时间。 - 可维护性:新人接手时,看标签就知道结构,不必逐个查 class 的用途。
- 体积:大量
class="post-title"之类的重复命名可以删掉,HTML 更短。 - 健壮性:即使 CSS 完全加载失败,页面依然保持正确的文档层级,可读性远高于纯 div 版本。
唯一需要注意的是:重构语义标签会改变 CSS 选择器。class 大量依赖标签名的项目,需要同步调整样式。推荐的做法是保留原有 class 作为样式钩子,只把标签换成语义元素——这样 CSS 完全不用改,收益却立刻到手。
十二、兼容性与渐进增强
HTML5 语义标签在今天已经可以放心使用,但仍有几个历史包袱值得了解。
IE8 及更早版本
这些老版本浏览器不认识 header、nav 等新标签,会把它们当作未知内联元素处理,导致样式失效。经典解决方案有两步:
<script>
document.createElement('header');
document.createElement('nav');
document.createElement('main');
document.createElement('article');
document.createElement('section');
document.createElement('aside');
document.createElement('figure');
document.createElement('figcaption');
document.createElement('footer');
</script>
/* 2. 让它们表现为块级元素 */
header, nav, main, article, section, aside, figure, figcaption, footer {
display: block;
}
今天几乎所有项目都已经不再需要这段代码,但了解它的存在有助于理解“为什么早期有人抗拒语义化标签”。
现代浏览器的支持情况
| 特性 | 支持情况 | 使用建议 |
|---|---|---|
| 结构标签(header/nav/main/…) | 全平台 | 放心使用 |
| 文本级标签(time/mark/abbr/…) | 全平台 | 放心使用 |
figure / figcaption |
全平台 | 放心使用 |
picture / source |
广泛支持 | 保留 img 兜底即可 |
details / summary |
广泛支持 | 放心使用,样式可用 CSS 定制 |
dialog(含 showModal) |
现代浏览器 | 老项目需准备降级方案 |
dialog 的 closedby |
较新特性 | 仅作增强,不要依赖 |
details 的 name 互斥 |
现代浏览器 | 作为增强,JS 方案兜底 |
template / slot |
广泛支持 | Web Components 的基础 |
渐进增强的思路
语义标签的天然优势在于:它们本身就是渐进增强的。即使浏览器不认识某个标签,它仍然是一个可以承载内容、可以被 CSS 选择器命中的元素。所以正确的心态是:
- 结构语义全部用原生标签,保证任何环境下都能工作。
- 样式与交互效果作为增强层,用
@supports或特性检测控制。 - 不要为了兼容一个占比极低的旧浏览器,放弃整个页面的语义结构。
十三、语义化检查清单与代码基线
把全文的结论浓缩成一份可以贴在工位上的清单。每次提交页面前扫一眼,能挡掉绝大多数问题。
- 页面只有一个
main,且没有被article/aside/nav包裹。 - 地标完整:页面有明确的
header、nav、main、footer区域。 - 标题层级不跳级:h1 → h2 → h3 依次递进,全页只有一个 h1。
- 每个
section都有标题,没有标题的容器请用div。 article判断标准:内容单独拿出来是否依然完整?- 导航用
nav+ul/li/a,不要用一堆div包链接。 - 按钮用
button,链接用a,绝不混用。 - 表单控件有
label,for与id一一对应。 - 图片有
alt,装饰性图片写alt=""。 - 时间用
time+datetime,机器可读。 - 引用用
blockquote/q,作品名用cite。 - 代码用
code/pre,键盘按键用kbd。 - 数据表格用
table,带caption与th scope。 - 不用
<br>制造间距,段落用p,间距交给 CSS。 - 不用
title传递关键信息,触屏与键盘用户拿不到。 - 保留焦点可见样式,不写裸的
outline: none。 - 只按 Tab 键走一遍页面,确认所有交互都能完成。
- 重构成语义标签时保留原有 class,让 CSS 无需改动。
<body>
<a href="#main" class="skip-link">跳到主内容</a>
<header>
<a href="/" class="logo">站点名</a>
<nav aria-label="主导航">
<ul>……</ul>
</nav>
</header>
<main id="main">
<article>
<header>
<h1>文章标题</h1>
<p>
<time datetime="2026-09-18">2026年9月18日</time>
</p>
</header>
<section>
<h2>第一节</h2>
<p>正文……</p>
<figure>
<img src="a.png" alt="描述" width="800" height="400">
<figcaption>图 1:说明文字</figcaption>
</figure>
</section>
<footer>
<small>本文采用 CC BY 4.0 协议</small>
</footer>
</article>
<aside aria-label="相关内容">
<h2>相关阅读</h2>
</aside>
</main>
<footer>
<small>© 2026 前端开发技巧</small>
</footer>
</body>
语义化最迷人的地方在于它的“不公平”:它几乎不需要额外的开发成本,却能同时改善可维护性、无障碍、SEO 与机器可读性。写下 <nav> 和写下 <div class="nav"> 的键盘敲击次数差不多,但它们对屏幕阅读器用户的意义,天差地别。
所以,下一次当你把手放上键盘准备敲 div 的时候,不妨先停半秒问自己一句:“这块内容,到底是什么?”——答案往往就是那个正确的标签。