应直接使用语义化HTML5标签如<header>、<nav>、<main>,因其默认隐式ARIA角色已满足可访问性要求;冗余添加role属性反而可能破坏辅助技术识别,仅在无对应标签时才显式声明role。

直接用 <header>、<nav>、<main> 就行,别乱加 role
绝大多数情况下,你不需要手动写 role="banner" 或 role="navigation" —— 因为 <header> 默认就是 role="banner",<nav> 默认是 role="navigation",<main> 是 role="main",<footer> 是 role="contentinfo"。浏览器和辅助技术(如 NVDA、VoiceOver)都认这些隐式角色。
常见错误是看到 ARIA 文档就一股脑给所有语义标签补 role,比如:<nav role="navigation">。这不仅冗余,还可能覆盖原生行为(尤其在旧版 IE 或某些屏幕阅读器组合下),反而破坏导航逻辑。
- 只在没有合适 HTML5 标签时才显式加
role,例如:<div role="search">(当搜索框不在<form>里,或需要单独标识为“全局搜索”) - 避免用
<div role="region">或<div role="group">包裹普通内容——它们不构成地标,只会让屏幕阅读器用户多出一堆无意义的跳转点 - 一个页面最多一个
role="main",且必须对应真正主要内容;多个<main>会触发可访问性检测工具报错
多个导航区域必须用 aria-label 区分
当页面有主导航、页脚导航、侧边栏导航甚至面包屑时,光靠 <nav> 不够——所有 <nav> 在辅助技术里默认都叫“navigation”,用户无法分辨哪个是主菜单、哪个是友情链接。
解决方法是给每个 <nav> 加 aria-label:
立即学习“前端免费学习笔记(深入)”;
<nav aria-label="主菜单">...</nav> <nav aria-label="页脚链接">...</nav> <nav aria-label="相关文章">...</nav>
注意:aria-label 值要简短明确(如“搜索”“筛选”),不能写“导航区域1”这种无意义描述;也不要用 aria-labelledby 指向一个不可见的 <span>,因为部分屏幕阅读器对隐藏文本支持不稳定。
<aside> 和 <section> 什么时候需要 role?
<aside> 默认是 role="complementary",<section> 默认没有地标角色——它只是通用分组容器,不是地标。但如果你的 <aside> 内容和主内容强关联(比如文章侧边的术语解释、作者简介),保留默认即可;如果它是独立模块(如广告、推荐位),建议显式加 role="complementary" 并配 aria-label,否则部分辅助技术可能忽略它。
<section> 只有在承担明确功能时才考虑加地标角色,例如:
- 一个带标题的“联系方式”区块 →
<section role="region" aria-label="联系我们"> - 一个动态加载的“实时评论区” →
<section role="region" aria-live="polite">
别给每个 <section> 都加 role="region":太多地标会让屏幕阅读器用户迷失,实际测试中发现超过 5 个地标区域后,跳转效率明显下降。
检查地标是否生效的最简方法
不用装插件或跑 Lighthouse——打开 Chrome,右键 → “检查”,切换到“Accessibility”面板,在“Landmarks”栏里直接看结构树。正常情况应显示:banner、navigation(带 label)、main、complementary、contentinfo 等节点,且无重复或缺失。
容易被忽略的点:
-
<main>必须是文档流中的第一个主要内容容器,不能被display: none或visibility: hidden隐藏,否则会被跳过 - 用 JavaScript 动态插入的地标(如 SPA 路由切换后新渲染的
<nav>),需确保 DOM 插入后立即可被辅助技术感知——延迟渲染或异步加载未完成时,地标可能暂时丢失 - 某些 CMS 输出的 HTML 会把
<header>套在<article>里,导致它变成 article 的 banner 而非整个页面的 banner,这时需手动加role="banner"并确保位置正确



















