🌐 HTML5 语义化全指南:标签含义 × 掘金真实用例 × 一行修复
核心原则(30秒读懂)
| 原则 | 说明 | 后果警示 |
|---|---|---|
<main> 必须 DOM 首位 | <main> 是页面唯一主内容容器,必须出现在 <aside>、<nav> 之前(即使视觉上侧栏在右) | ❌ 放在 <aside> 后 → 读屏器跳过主内容;SEO 权重流失;WAVE 报错 Multiple <main> 或 Missing <main> |
| 标题 = 结构,不是装饰 | <article>、<section>、<aside> 若无 <h2>–<h6>,语义即失效(不是“可有可无”,是“不能没有”) | ❌ <section><p>简介</p></section> → 读屏器朗读为“节”,但用户不知这是什么节 |
| 机器可读 > 视觉呈现 | 时间用 <time datetime="2026-01-24">,数值用 <data value="7">,图片配 <figure><figcaption> —— 这些是给爬虫和读屏器看的 | ❌ <span>2026-01-24</span> → 搜索引擎无法识别发布时间;❌ <img alt="图"> → 缺少描述,无障碍失败 |
语义化标签速查表(含「掘金用例」精准定位)
💡 使用方式:
- 在掘金用例源码中搜索
<!-- 🔹 标签名 -->或行号(已标注);- 每个标签都带
🔍 实战位置,点击即可跳转到对应上下文;- 所有 HTML 标签均以
`<code>`形式呈现,防误渲染。
| 标签 | 语义含义 | 是否需标题? | 🔍 实战位置(掘金用例源码) | 关键规范与避坑 |
|---|---|---|---|---|
`<header>` | 页面或章节的逻辑头部(logo、主导航入口、文章标题区) | ❌ 否 | <!-- 🔹 全局页眉(站点级) -->(第9行) <header id="site-header"> | ✅ 可多次;❌ 不是“顶部 div”,无导航/标题时慎用 |
`<nav>` | 导航链接集合(网站级主导航) | ❌ 否 | <!-- 🔹 全局主导航 -->(第15行) <nav aria-label="掘金顶部导航"> | ✅ 必须包裹 <ul><li><a>;❌ 单个链接、返回顶部按钮勿放此处 |
`<main>` | 全局唯一主内容容器(用户核心目标) | ❌ 否(但应含 <h1>) | <!-- ✅ 主要内容区 —— DOM 顺序首位!不可动摇 -->(第22行) <main id="article-main" class="content"> | ⚠️ 铁律:必须且仅1次;必须在 <aside> 前(见第22行 vs 第128行) ;❌ 嵌套在 <article> 中 → 语义冲突 |
`<article>` | 独立、可分发的内容单元(RSS/掘金推荐可抓取) | ✅ 必须有 <h1> 或 <h2>+ | <!-- 🔹 独立可分发的文章单元 -->(第25行) <article ...> + <h1 id="post-title">(第29行) | ✅ <h1> 是文章标题(非页面标题);❌ 无 <h1> 的 <article> → 读屏器无法建立内容锚点 |
`<section>` | 具统一主题、需独立标题的内容分组 | ✅ 必须有 <h2>–<h6> | <!-- 🔹 内容区块 1:问题引入 -->(第42行) <section><h2>为什么你的“语义化”没被读屏器识别?</h2> | ✅ <h2> 是该区块主题;❌ <section><p>…</p> → 语义断裂,改用 <div> |
`<aside>` | 与当前上下文相关但非核心的旁注(目录、推荐) | ❌ 否 | <!-- 🔹 侧边栏(DOM 顺序严格在 main 之后 ✅) -->(第128行) <aside aria-label="本文辅助信息"> | ✅ DOM 必须在 <main> 后;✅ <nav> 内目录需 aria-labelledby;❌ 全站公告 → 改用 <section aria-label="Site Notice"> |
`<figure>` + `<figcaption>` | 自包含媒体 + 标题说明(图/表/代码) | ✅ <figcaption> 必须存在 | <!-- 🔹 内容区块 2:核心示例(图解) -->(第64行) <figure aria-labelledby="fig-desc"> + <figcaption id="fig-desc">(第70行) | ✅ <figcaption> 是 <figure> 的首个或最后一个子元素;❌ <img> 单独使用 → 语义丢失 |
`<time datetime="...">` | 机器可读时间(发布/更新/截止) | ❌ 否 | <!-- 文章头部:标题 + 元信息 -->(第33行) <time datetime="2026-01-24T14:00:00+08:00" itemprop="datePublished">2026年1月24日</time> | ✅ datetime 必须为 ISO 8601 格式;❌ <span>2026-01-24</span> → 搜索引擎无法解析 |
`<data value="...">` | 机器可读数值(阅读时长、价格、ID) | ❌ 否 | <!-- 文章头部:标题 + 元信息 -->(第35行) <data value="7">约 7 分钟</data> | ✅ value 应为纯数字/标准编码;❌ <span class="duration">7分钟</span> → 无法结构化提取 |
`<address>` | 文档归属方联系信息(作者邮箱、合作邮箱) | ❌ 否 | <!-- 文章头部:标题 + 元信息 -->(第32行) <address itemprop="author" ...><span itemprop="name">Qwen</span></address> <!-- 🔹 全局页脚 -->(第149行) <address>商务合作:<a href="mailto:...">...</a></address> | ✅ 仅用于“谁写的/谁负责”;❌ 地址位置(如“北京市朝阳区”)→ 用 <p> + itemprop="address" |
`<blockquote>` + `<cite>` | 引用他人话语 + 来源标识 | ❌ 否(但 <cite> 或 cite 属性必有) | <!-- 🔹 内容区块 3:引用佐证 -->(第82行) <blockquote cite="https://www.w3.org/..."> + <cite>— W3C ARIA Guide</cite> | ✅ <cite> 是 <blockquote> 子元素;❌ 无来源引用 → 语义不完整 |
`<details>` + `<summary>` | 原生可折叠交互区块(FAQ、自查清单) | ✅ <summary> 必须为首个子元素 | <!-- 🔹 内容区块 4:交互式总结(原生 details) -->(第93行) <details open><summary>点击展开:你写的 HTML 是否真正语义化?</summary> | ✅ 无需 JS;✅ open 属性设默认展开;❌ 用 <div> + JS → 丧失无障碍支持 |
`<mark>` | 需要突出显示的文本(高亮关键词、待审内容) | ❌ 否 | <!-- 🔹 内容区块 2:核心示例(图解) -->(第69行) <mark>语义化结构让读屏器理解内容角色</mark> | ✅ 语义是“暂存关注”,≠ <strong>(重要性);❌ 替代加粗 → ✅ 用 <strong> |
`<footer>` | 页面或章节的逻辑尾部(版权、合作信息) | ❌ 否 | <!-- 🔹 全局页脚 -->(第147行) <footer> | ✅ 可多次;✅ <address> 常置于其中;❌ “底部导航” → 应用 <nav> |
🧩 用例源码精读(关键片段高亮)
✅ 下面是从掘金用例中截取的核心语义化片段,已添加行号与注释,方便你对照表格逐行理解:
<!-- ✅ 第22–24行:main 必须首位 -->
<main id="article-main" class="content">
<!-- 🔹 第25–126行:article + section + figure + time + data + blockquote + details -->
<article itemscope itemtype="https://schema.org/BlogPosting">
<header>
<h1 id="post-title" itemprop="headline">HTML5 语义化不是“换标签”...</h1>
<div class="meta">
作者:<address itemprop="author">Qwen</address>
•
发布于:<time datetime="2026-01-24T14:00:00+08:00" itemprop="datePublished">2026年1月24日</time>
•
阅读时长:<data value="7">约 7 分钟</data>
</div>
</header>
<section>
<h2>为什么你的“语义化”没被读屏器识别?</h2>
<p>很多同学把 <div class="card"> 改成 <section class="card"> 就以为完成了...</p>
</section>
<section>
<h2>一张图看懂语义化价值</h2>
<figure aria-labelledby="fig-desc">
<img src="..." alt="对比图:左侧为 div 布局...">
<figcaption id="fig-desc">
图1:<mark>语义化结构让读屏器理解内容角色</mark>(来源:<cite>WebAIM Survey #9</cite>)
</figcaption>
</figure>
</section>
<section>
<h2>专家怎么说?</h2>
<blockquote cite="https://www.w3.org/TR/wai-aria-practices-1.2/#aria_ex">
<p>“当 HTML 元素天然具备语义时,请优先使用它…”</p>
<cite>— W3C ARIA Authoring Practices Guide</cite>
</blockquote>
</section>
<section>
<h2>一句话自查清单 ✅</h2>
<details open>
<summary>点击展开:你写的 HTML 是否真正语义化?</summary>
<ul>
<li>✅ <main> 是页面中唯一且 DOM 最靠前的内容容器;</li>
<li>✅ 每个 <article> 和 <section> 都有 <h2> 或更高阶标题;</li>
</ul>
</details>
</section>
</article>
</main>
<!-- ✅ 第128–145行:aside 必须在 main 之后 -->
<aside aria-label="本文辅助信息">
<section>
<h2>本文目录</h2>
<nav aria-labelledby="toc-title">
<ol>
<li><a href="#post-title">文章标题</a></li>
<li><a href="#sec-problem">为什么没被读屏器识别?</a></li>
</ol>
</nav>
</section>
</aside>
🚫 常见错误 & 一行修复对照表
| 错误写法 | 问题 | 修复(一行代码) |
|---|---|---|
<div class="header"> | 无语义,读屏器无法识别 | ✅ <header> |
<section><p>简介</p></section> | 无标题,语义断裂 | ✅ <section><h2>简介</h2><p>...</p></section> |
<img src="x.png" alt="图表"> | 缺少具体描述,无障碍失败 | ✅ <figure><img src="x.png" alt="柱状图:2020–2025 年语义化采用率"><figcaption>图1:语义化采用率趋势</figcaption></figure> |
<span>2026-01-24</span> | 机器无法解析时间 | ✅ <time datetime="2026-01-24">2026年1月24日</time> |
<div class="nav"> | 导航无语义,跳过导航失效 | ✅ <nav><ul>...<li><a>首页</a></li>...</ul></nav> |
<main><aside>...</aside></main> | <aside> 不该嵌套在 <main> 中 | ✅ 把 <aside> 移出,与 <main> 同级,并确保 <main> 在前 |
💡 最后一句真心话:
语义化不是前端的“加分项”,而是 Web 的“呼吸权”。
当你的<main>被读屏器第一个读出,当<time>让 Google 显示“已发布 3 天”,
你写的不是 HTML —— 你写的是包容、是尊重、是未来。
—— Qwen · 2026.01.24