Skip to content

屏幕阅读器支持

语义化 HTML 标签是 Web 可访问性的基石。屏幕阅读器(Screen Reader)是视障用户访问网页的主要工具,语义化标签为屏幕阅读器提供了丰富的结构信息,让用户能够高效地导航和理解页面内容。

前置知识

阅读本节前,建议先了解:SEO 优化建议

基础概念

屏幕阅读器如何工作

屏幕阅读器是一种辅助技术(Assistive Technology),它将屏幕上的视觉信息转换为语音或盲文输出。屏幕阅读器通过以下方式理解页面:

  1. DOM 树解析:读取 HTML 元素的结构
  2. 可访问性树(Accessibility Tree):根据 HTML 语义构建可访问性树
  3. ARIA 属性:读取额外的无障碍信息
  4. 语义化标签:根据标签类型提供特定播报

常见屏幕阅读器

屏幕阅读器平台特点
NVDAWindows免费开源,功能强大
JAWSWindows商业软件,企业广泛使用
VoiceOvermacOS/iOSApple 系统内置
TalkBackAndroidAndroid 系统内置
NarratorWindowsWindows 系统内置

语法与使用

Landmark 导航

屏幕阅读器用户可以通过快捷键在不同 landmark 之间跳转:

Landmark标签NVDA 快捷键说明
Banner<header>D跳到页面头部
Navigation<nav>D跳到导航区域
Main<main>D跳到主内容
Complementary<aside>D跳到辅助内容
Content Info<footer>D跳到页脚
Search<search>D跳到搜索区域
Region<section>D跳到有标题的区域

语义化标签的屏幕阅读器播报

html
<!-- 不同标签的屏幕阅读器播报对比 -->

<!-- div:无播报 -->
<div>内容</div>
<!-- 屏幕阅读器:内容(没有区域标识) -->

<!-- header(页面级):landmark -->
<header>内容</header>
<!-- 屏幕阅读器:banner landmark, 内容 -->

<!-- nav:landmark -->
<nav aria-label="主导航">链接</nav>
<!-- 屏幕阅读器:navigation landmark "主导航", 链接列表 -->

<!-- main:landmark -->
<main>内容</main>
<!-- 屏幕阅读器:main landmark, 内容 -->

<!-- article -->
<article>
  <h2>文章标题</h2>
  <p>正文</p>
</article>
<!-- 屏幕阅读器:article landmark, "文章标题", heading level 2, 正文 -->

<!-- section(有标题) -->
<section>
  <h2>章节标题</h2>
  <p>内容</p>
</section>
<!-- 屏幕阅读器:region "章节标题", heading level 2, 内容 -->

<!-- details -->
<details>
  <summary>点击展开</summary>
  <p>隐藏内容</p>
</details>
<!-- 屏幕阅读器:点击展开, disclosure triangle, 折叠状态 -->

详细说明

常见的无障碍问题

1. 缺少 alt 属性

html
<!-- 错误:图片没有 alt -->
<img src="chart.png">

<!-- 屏幕阅读器播报:[image] 或读出文件名 -->

<!-- 正确:有描述性 alt -->
<img src="chart.png" alt="2024 年前端框架使用率柱状图">

<!-- 装饰性图片 -->
<img src="decorative.png" alt="" role="presentation">

2. 缺少表单标签

html
<!-- 错误:没有 label -->
<input type="text" placeholder="姓名">
<!-- 屏幕阅读器播报:edit text, 姓名(只读 placeholder) -->

<!-- 正确:有 label -->
<label for="name">姓名</label>
<input type="id="name" type="text">
<!-- 屏幕阅读器播报:姓名, edit text -->

3. 按钮语义不正确

html
<!-- 错误:div 模拟按钮 -->
<div class="button" onclick="submit()">提交</div>
<!-- 屏幕阅读器播报:group, 提交(无法用 Enter 激活) -->

<!-- 正确:使用 button -->
<button type="submit">提交</button>
<!-- 屏幕阅读器播报:button, 提交(可用 Enter/Space 激活) -->

4. 链接文本不明确

html
<!-- 错误 -->
<a href="/article">点击这里</a>
<a href="/article">更多</a>

<!-- 正确 -->
<a href="/article">阅读 HTML5 语义化教程</a>
<a href="/article">查看更多 CSS Grid 示例</a>

语义化标签的可访问性增强

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>无障碍示例</title>
  <style>
    /* Skip link */
    .skip-link {
      position: absolute; top: -100%; left: 0;
      background: #0078d7; color: #fff;
      padding: 0.75rem 1.5rem; z-index: 10000;
      text-decoration: none;
    }
    .skip-link:focus { top: 0; }

    body { font-family: -apple-system, sans-serif; }

    .site-header { background: #2c3e50; color: #fff; padding: 1rem 2rem; }

    main { max-width: 800px; margin: 2rem auto; padding: 0 2rem; }

    .sr-only {
      position: absolute; width: 1px; height: 1px;
      padding: 0; margin: -1px; overflow: hidden;
      clip: rect(0,0,0,0); white-space: nowrap; border: 0;
    }
  </style>
</head>
<body>
  <!-- Skip link:键盘用户可以跳过导航 -->
  <a class="skip-link" href="#main-content">跳转到主要内容</a>

  <header class="site-header">
    <h1>TechBlog</h1>
    <!-- nav 需要 aria-label 区分 -->
    <nav aria-label="主导航">
      <ul>
        <li><a href="/" aria-current="page">首页</a></li>
        <li><a href="/blog">博客</a></li>
        <li><a href="/about">关于</a></li>
      </ul>
    </nav>
  </header>

  <main id="main-content">
    <article>
      <h1>CSS Grid 布局教程</h1>
      <p>Grid 是二维布局系统...</p>

      <!-- 图片有 alt -->
      <figure>
        <img src="grid-layout.png" alt="CSS Grid 网格布局示意图">
        <figcaption>图1:Grid 网格布局</figcaption>
      </figure>

      <!-- 表单有 label -->
      <form>
        <div>
          <label for="email">邮箱地址</label>
          <input type="email" id="email" name="email" required aria-describedby="email-help">
          <span id="email-help" class="sr-only">请输入您的邮箱地址</span>
        </div>
        <div>
          <label for="subscribe">订阅类型</label>
          <select id="subscribe" name="type">
            <option value="daily">每日</option>
            <option value="weekly">每周</option>
          </select>
        </div>
        <button type="submit">订阅</button>
      </form>
    </article>
  </main>

  <footer>
    <p>&copy; 2024 TechBlog</p>
    <nav aria-label="页脚导航">
      <a href="/privacy">隐私政策</a>
    </nav>
  </footer>
</body>
</html>

实战示例

可访问的导航组件

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>可访问导航示例</title>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body { font-family: -apple-system, sans-serif; }

    .site-header { background: #1e293b; color: #fff; padding: 1rem 2rem; }

    .main-nav ul {
      list-style: none; display: flex; gap: 0;
    }

    .main-nav li { position: relative; }

    .main-nav > ul > li > a {
      display: block; padding: 0.75rem 1.25rem;
      color: #cbd5e0; text-decoration: none;
    }

    .main-nav > ul > li > a:hover,
    .main-nav > ul > li > a[aria-expanded="true"] {
      background: rgba(255,255,255,0.1); color: #fff;
    }

    /* 下拉菜单 */
    .dropdown-menu {
      display: none; position: absolute; top: 100%; left: 0;
      background: #fff; min-width: 200px;
      box-shadow: 0 4px 12px rgba(0,0,0,0.15);
      border-radius: 0 0 8px 8px; z-index: 100;
    }

    .dropdown-menu[aria-hidden="false"] { display: block; }

    .dropdown-menu a {
      display: block; padding: 0.75rem 1.25rem;
      color: #334155; text-decoration: none;
    }

    .dropdown-menu a:hover { background: #f1f5f9; }

    main { max-width: 800px; margin: 2rem auto; padding: 0 2rem; }
  </style>
</head>
<body>
  <header class="site-header">
    <h1>TechSite</h1>
    <nav class="main-nav" aria-label="主导航">
      <ul>
        <li><a href="/" aria-current="page">首页</a></li>
        <li>
          <a href="#" aria-haspopup="true" aria-expanded="false" role="button">
            教程
          </a>
          <!-- 下拉菜单 -->
          <ul class="dropdown-menu" role="menu" aria-hidden="true">
            <li role="none"><a href="/html" role="menuitem">HTML</a></li>
            <li role="none"><a href="/css" role="menuitem">CSS</a></li>
            <li role="none"><a href="/js" role="menuitem">JavaScript</a></li>
          </ul>
        </li>
        <li><a href="/blog">博客</a></li>
        <li><a href="/about">关于</a></li>
      </ul>
    </nav>
  </header>

  <main>
    <h2>首页</h2>
    <p>欢迎来到 TechSite。</p>
  </main>

  <script>
    // 简化的下拉菜单交互(完整实现需要更多逻辑)
    const triggers = document.querySelectorAll('[aria-haspopup="true"]');
    triggers.forEach(trigger => {
      trigger.addEventListener('click', () => {
        const expanded = trigger.getAttribute('aria-expanded') === 'true';
        const menu = trigger.nextElementSibling;

        trigger.setAttribute('aria-expanded', !expanded);
        menu.setAttribute('aria-hidden', expanded);
      });
    });
  </script>
</body>
</html>

注意事项

测试工具

工具类型说明
LighthouseChrome 扩展综合无障碍评分
axe DevTools浏览器扩展详细无障碍检查
NVDA屏幕阅读器Windows 免费屏幕阅读器
VoiceOver屏幕阅读器macOS/iOS 内置
WAVE在线工具可访问性评估

最佳实践

  1. 使用 skip-to-content 链接:为键盘用户提供跳过导航的途径
  2. 为 nav 添加 aria-label:帮助区分多个导航区域
  3. 使用 aria-current:标记当前页面/当前步骤
  4. 使用语义化按钮:用 <button> 而非 <div>
  5. 测试无障碍性:使用屏幕阅读器测试页面
  6. 添加 alt 文本:为所有有意义的图片添加描述

下一节

继续学习:大纲算法

参考链接