HTML 语义化与 ARIA
这一课你将能做到
- 用 HTML5 语义标签(
<header>、<nav>、<main>、<article>、<section>、<aside>、<footer>)构建页面骨架 - 为复杂组件补充 ARIA 属性(
role、aria-label、aria-expanded、aria-live等),提升无障碍访问体验 - 理解语义化对 SEO、屏幕阅读器和代码可维护性的综合价值
为什么要学
HTML5 引入了大量语义标签,不是为了让你多记几个单词,而是为了解决一个根本问题:「这段内容到底是什么?」搜索引擎爬虫、屏幕阅读器、RSS 阅读器、社交分享机器人——它们都看不到 CSS 渲染后的视觉效果,只能读取 HTML 标签来判断内容结构。
CodeForge 的课程列表页面如果用 <div class="card"> 堆砌,搜索引擎只知道「这里有几个 div」,不知道「这是课程卡片、有标题、有描述、有价格」。换成 <article> + <h2> + <p> + <time>,信息的可读性和可访问性会完全不同。
语义化和 ARIA 不是「加分项」,而是现代 Web 开发的底线要求。在很多国家和地区,Web 可访问性(Accessibility,简称 a11y)是法律要求。
先建立图像
CodeForge 首页语义化结构:
<body>
├── <header>
│ └── <nav aria-label="主导航"> ... </nav>
│
├── <main>
│ ├── <section aria-labelledby="hero-title">
│ │ └── <h1 id="hero-title">...</h1>
│ │
│ ├── <section aria-labelledby="courses-title">
│ │ └── <h2 id="courses-title">热门课程</h2>
│ │ └── <ul>
│ │ └── <li>
│ │ └── <article>
│ │ ├── <h3>课程标题</h3>
│ │ ├── <p>描述</p>
│ │ └── <time datetime="2026-10-07">...</time>
│ │
│ └── <aside aria-label="侧边栏">
│ └── <p>学习提示...</p>
│
└── <footer> ... </footer>
跟着做
步骤 1:用语义标签重写页面骨架
创建 semantic.html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>语义化示例 | CodeForge</title>
<style>
:root { --primary: #2563eb; --surface: #fff; --text: #0f172a; --muted: #64748b; --border: #e2e8f0; }
body { font-family: system-ui, sans-serif; margin: 0; color: var(--text); background: #f8fafc; }
header, footer { background: var(--surface); border-bottom: 1px solid var(--border); padding: 1rem 2rem; }
footer { border-top: 1px solid var(--border); border-bottom: none; text-align: center; color: var(--muted); }
nav ul { display: flex; gap: 1.5rem; list-style: none; margin: 0; padding: 0; }
nav a { color: var(--text); text-decoration: none; font-weight: 500; }
main { max-width: 900px; margin: 2rem auto; padding: 0 1rem; }
.hero { text-align: center; padding: 3rem 1rem; }
.hero h1 { font-size: clamp(1.75rem, 4vw, 2.5rem); margin: 0 0 0.5rem; }
.course-grid { display: grid; gap: 1.5rem; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); margin-top: 1.5rem; }
article { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 1.5rem; }
article h3 { margin: 0 0 0.5rem; font-size: 1.125rem; }
article p { margin: 0 0 0.75rem; color: var(--muted); font-size: 0.9375rem; }
time { font-size: 0.875rem; color: var(--muted); }
aside { background: #eff6ff; border-left: 4px solid var(--primary); padding: 1rem 1.25rem; border-radius: 0 8px 8px 0; margin-top: 2rem; }
</style>
</head>
<body>
<header>
<nav aria-label="主导航">
<ul>
<li><a href="#" aria-current="page">首页</a></li>
<li><a href="#">课程</a></li>
<li><a href="#">路线图</a></li>
<li><a href="#">关于</a></li>
</ul>
</nav>
</header>
<main>
<section class="hero" aria-labelledby="hero-title">
<h1 id="hero-title">构建你的前端技能树</h1>
<p>从 HTML 语义化到现代 CSS 布局,系统化掌握网页开发。</p>
</section>
<section aria-labelledby="courses-title">
<h2 id="courses-title">热门课程</h2>
<div class="course-grid">
<article>
<h3>HTML/CSS 基础精讲</h3>
<p>掌握语义化标签、盒模型、Flexbox 与 Grid 布局。</p>
<time datetime="2026-10-07">更新于 2026年10月7日</time>
</article>
<article>
<h3>JavaScript 核心原理</h3>
<p>深入理解作用域、闭包、原型链与异步编程。</p>
<time datetime="2026-09-20">更新于 2026年9月20日</time>
</article>
</div>
</section>
<aside aria-label="学习建议">
<p><strong>提示:</strong>建议按顺序学习基础课程,再进入进阶内容。</p>
</aside>
</main>
<footer>
<p>© 2026 CodeForge 学习平台. 保留所有权利。</p>
</footer>
</body>
</html>
保存后打开,按 F12 → Elements 观察标签树。用 axe DevTools 或 Lighthouse 运行可访问性审计,得分应该很高。
步骤 2:理解每个语义标签的职责
| 标签 | 用途 | 不要这样用 |
|---|---|---|
<header> | 页面或区块的引导内容(logo、导航、搜索) | 仅为了放大顶部背景 |
<nav> | 主要导航链接组 | 把页脚所有链接都包进去 |
<main> | 页面唯一的主要内容区 | 一个页面出现多个 <main> |
<article> | 可独立分发、复用的内容块(博客、课程卡片、评论) | 纯装饰性容器 |
<section> | 主题相关的分组,通常带标题 | 单纯为了加样式而分组(用 <div>) |
<aside> | 与周围内容间接相关(侧边栏、提示框) | 主要内容区 |
<footer> | 页面或区块的尾部信息(版权、相关链接) | 页面任意位置的底部 |
步骤 3:为导航添加 ARIA 当前状态
<nav aria-label="主导航">
<ul>
<li><a href="/" aria-current="page">首页</a></li>
<li><a href="/courses">课程</a></li>
</ul>
</nav>
aria-current="page" 告诉屏幕阅读器「这是当前所在页面」,用户不用猜测自己在哪。同理,分页组件中当前页可用 aria-current="true"。
步骤 4:为可展开内容添加 ARIA 状态
<button type="button" aria-expanded="false" aria-controls="course-menu" id="menu-toggle">
展开课程目录
</button>
<div id="course-menu" role="region" aria-labelledby="menu-toggle" hidden>
<ul><li>第 1 课:网页如何工作</li><li>第 2 课:HTML 文档结构</li></ul>
</div>
aria-expanded 表示展开状态,aria-controls 指向被控制的元素 ID。当用户点击按钮展开内容时,JavaScript 应将 aria-expanded 改为 "true",并移除 hidden 属性。屏幕阅读器会播报「展开,按钮」或「折叠,按钮」。
步骤 5:使用 aria-live 播报动态更新
<div id="toast-region" aria-live="polite" aria-atomic="true"></div>
当 JavaScript 向这个区域插入内容(如「保存成功」「网络错误」)时,屏幕阅读器会自动朗读。polite 表示等用户当前操作完成后再播报,不会打断;assertive 表示立即播报,仅用于关键错误。
讲清楚要点
-
语义化首先是给人读的:语义标签让代码结构一目了然。看到
<article>你就知道这是独立内容块,看到<nav>就知道这是导航。维护成本显著低于满屏<div class="nav">、<div class="main">。 -
<section>需要标题:如果一个<section>没有标题(<h2>-<h6>),那它很可能不该用<section>,用<div>更合适。<section>的存在意义就是「这是文档大纲中的一个主题分组」。 -
<article>强调可独立复用:一篇博客文章、一条评论、一个课程卡片、一条搜索结果,都是<article>。测试方法:如果这段内容被单独分享到其他地方仍然有意义,它就是<article>。 -
ARIA 是补充,不是替代:优先使用原生 HTML 语义标签。如果原生标签不够用(如自定义下拉菜单、标签页组件),再用 ARIA 补充。不要给
<button>加role="button",它是多余的。 -
可访问性是法律责任,也是商业需求:在许多国家(包括中国),政府网站和公共服务必须满足 WCAG 标准。对于商业产品,无障碍改进通常能覆盖 15%-20% 的潜在用户(包括老年人、临时受伤者、低视力者)。
你可能会懵的地方
-
「都用
<div>也能显示,为什么要多记这么多标签?」 为了代码可读性、SEO、无障碍访问和未来可维护性。一个项目活过三个月,开发者就会感谢当初写语义标签的人。搜索引擎也会感谢——排名可能更高。 -
<section>和<div>到底怎么选?」 如果这段内容需要一个标题来概括主题,用<section>;如果只是为了 CSS 布局或 JS 操作而需要一个容器,用<div>。简单规则:没有标题的<section>是可疑的。 -
「
<article>和<section>可以互相嵌套吗?」 可以,但要符合逻辑。<section>里可以包多个<article>(如「评论区」是一个 section,每条评论是一个 article)。<article>里也可以有<section>(如一篇长文章分「引言」「方法」「结论」三个 section)。 -
「ARIA 写多了会有副作用吗?」 会。错误的 ARIA 比没有 ARIA 更糟。例如给
<div>加role="button"但不处理键盘事件(Enter/Space),会让屏幕阅读器用户以为这是按钮,但无法操作。ARIA 的第一原则是:能用原生 HTML 元素就不用 ARIA。 -
「
aria-label和aria-labelledby有什么区别?」aria-label直接提供文本字符串,如aria-label="关闭弹窗"。aria-labelledby引用页面上另一个元素的 ID,如aria-labelledby="dialog-title",这样标签文本可以复用已有内容,且支持多语言。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
全站只用 <div> 和 <span> | 搜索引擎和屏幕阅读器无法理解结构 | 根据内容含义选择语义标签 |
一个页面多个 <main> | 违反规范,辅助技术无法识别主内容 | 每页仅一个 <main> |
<nav> 包进页脚所有链接 | <nav> 应只用于主要导航区块 | 页脚链接用 <footer> 内的 <ul> 即可 |
给原生按钮加 role="button" | 冗余,且可能覆盖原生行为 | 原生元素不要加冗余 role |
<section> 没有标题 | 失去大纲意义,不如用 <div> | 每个 section 配一个标题 |
用 aria-hidden="true" 隐藏焦点元素 | 键盘用户仍能聚焦到「看不见」的元素 | 隐藏时同时从 Tab 顺序中移除(tabindex="-1" 或 display:none) |
卡点急救
-
Lighthouse 可访问性分数很低:常见原因包括图片缺少
alt、表单字段没有label、颜色对比度不足(文字与背景色对比度应至少 4.5:1)、可点击元素尺寸太小(推荐至少 44×44 CSS 像素)。 -
屏幕阅读器不播报当前位置:检查导航链接是否有
aria-current="page";检查标题层级是否连续(不要从<h1>跳到<h3>)。 -
自定义组件键盘无法操作:所有交互元素必须是可聚焦的(
<button>、<a>、带tabindex="0"的元素)。确保可以通过Tab聚焦,Enter或Space激活,且焦点顺序符合视觉顺序。
面试常问
-
「什么是 HTML 语义化?常见的语义标签有哪些?」 HTML 语义化是指使用具有明确含义的标签来描述内容结构,而不是仅为了视觉效果。常见语义标签包括:
<header>(页头)、<nav>(导航)、<main>(主内容)、<article>(独立内容块)、<section>(主题分组)、<aside>(侧边内容)、<footer>(页脚)、<time>(时间)。语义化有利于 SEO、无障碍访问和代码可维护性。 -
「ARIA 是什么?什么时候需要使用?」 ARIA(Accessible Rich Internet Applications)是一套属性,用于补充 HTML 语义,让辅助技术更好地理解复杂组件和动态内容。当原生 HTML 元素无法满足需求时使用,例如自定义下拉菜单、标签页、模态框、动态加载内容等。第一原则是「能用原生 HTML 就不用 ARIA」。常用属性包括
role、aria-label、aria-labelledby、aria-expanded、aria-hidden、aria-live。 -
「如何测试网页的可访问性?」 三层测试:一、自动化工具(Lighthouse、axe DevTools),快速捕获标签缺失、对比度不足;二、键盘测试,断开鼠标,只用
Tab、Enter、Space、方向键完成所有交互;三、屏幕阅读器测试(NVDA、VoiceOver),实际听读页面内容,验证标签和动态更新是否正确播报。
自测三问
- 你正在构建一个博客网站,首页有文章列表、侧边栏分类、页脚版权信息。请指出哪些部分应该使用
<article>、<aside>、<section>、<nav>。 - 为什么给
<div onclick="...">加role="button"是不够的?还需要做什么才能让它真正可访问? aria-live="polite"和aria-live="assertive"有什么区别?分别适用于什么场景?
小结一句话
HTML5 语义标签让代码自己说话,ARIA 让复杂交互也能被所有人理解;语义化不是「额外的功夫」,而是专业前端工程师的基本功。
延伸
- MDN:HTML5 语义元素
- MDN:ARIA 入门
- 下一课:CSS 基础——从选择器到层叠与继承。