正确使用语义化HTML标签比ARIA属性更有效,但错用如<main>嵌套在<table>中会导致其被DOM丢弃;<nav>须包裹可聚焦元素且多导航需aria-label区分;<section>必须以h2–h6开头并保持标题层级连续;<fieldset>和<label>需严格遵循嵌套规则,否则表单可访问性失效。

直接说结论:用对 <header>、<nav>、<main>、<footer>、<section>、<article> 这六个标签,比加一百个 aria- 属性更管用;但错用其中任何一个,屏幕阅读器就可能“读丢”整块内容。
为什么 <main> 必须全局唯一且不能嵌套在 <table> 里
浏览器解析 HTML 时,如果发现 <main> 被包在 <table> 或 <ul> 里,会直接丢弃该节点——不是忽略语义,而是整个元素从 DOM 树里消失。屏幕阅读器根本看不到它。
-
<main>只能作为<body>的直接子元素,或嵌套在<article>/<section>内(但此时它代表该局部区块的主体,非常规用法) - 多个
<main>会导致可访问性树结构混乱,NVDA 和 VoiceOver 都会报 warning - 误把整页内容包进
<section>后再塞<main>,等于没用:因为<section>本身无“主内容”权重,AT 不会优先跳转
<nav> 不是写个 class="nav" 就算数,得满足三个硬条件
很多页面写了 <nav> 却仍被读屏跳过,问题不在 ARIA,而在基础结构失效。
- 必须直接包裹可聚焦元素:
<a>、<button>或带tabindex="0"的元素;纯文字或<span>不行 - 不能嵌套在
<table>、<ul>、<div role="group">等限制子元素的容器中 - 同一页面有多个导航区(如顶部主导航 + 页脚链接 + 面包屑),必须用
aria-label显式区分,例如<nav aria-label="面包屑">
标题层级断层和 <section> 开头没 <h2>–<h6> 是隐形陷阱
屏幕阅读器用户靠 H 键快速跳转标题建立内容地图。一旦层级断裂或 <section> 没标题,这块区域就退化为普通 <div>,连“这是个章节”的提示都没有。
立即学习“前端免费学习笔记(深入)”;
- 必须连续:从
<h1>→<h2>→<h3>,禁止<h2>后接<h1>或跳到<h4> -
<section>不是“万能包装盒”,它必须以<h2>–<h6>开头,否则语义丢失 - 避免
<section>套<section>再套<h1>:每个<section>应有自己的层级起点,且不应高于其父级
<label> 和 <fieldset> 用 <div> 包裹就等于放弃表单可访问性
用 <div class="form-group"> 替代 <fieldset>,再加 role="group" 和 aria-labelledby,旧版 JAWS 8.0 或 iOS 12 VoiceOver 会完全忽略分组标题。
-
<label>必须显式绑定<input>:用for/id,或直接包裹,仅靠视觉位置或aria-label不稳定 -
<fieldset>必须是<input>的直接父容器,不能被<div>包一层再设role -
<legend>是必填项,不可省略;空<legend>比没有还糟——部分读屏会重复播报“空白分组”
最常被忽略的一点:所有语义标签的效力都依赖于真实 DOM 结构有效性。HTML 解析器不校验语义,但会静默丢弃非法嵌套——你以为写的 <main> 其实根本没进树里。验证前先用浏览器开发者工具的“Accessibility”面板看可访问性树,比任何代码检查都直接。



















