无障碍化 Accessibility

Posted on Mar 21, 2024

什么是 Accessibility?

  • 以任何方式访问网络的用户(移动端、电视、手表…),无论听觉、视觉、身体和认知能力如何(视障、听障、运动功能障碍…),应用程序都是可以理解和使用的
  • 网站不应该造成伤害(诱发光敏性癫痫等)

可以从什么方面作出努力?

HTML

语义化结构

  1. <button> vs <div>

提供了默认样式、提供了键盘的无障碍(tab 键更换按钮、enter 键点击按钮)

  1. <h1> <h2> <p> vs <font> <br> <hr> <i>

便于屏幕阅读器识别标题和段落结构 🆚 没有任何可定位的标记的一大坨

  1. 替代文本
  • alt="" 属性,包含对图像的描述,向无法看到/听到某些内容的用户传达其含义和内容。如果 alt 为空,表示该图像只用于装饰(或 role =“presentation” 阻止辅助设备读出替代文本)

  • title 属性,屏幕阅读器也会读出 title 的内容

  • <track> 元素向视频/音频添加文本轨道

  1. 提供元素之间的联系和上下文,例如 <label for=""><input>

WAI-ARIA 属性

当原生 HTML 元素无法满足需求时,考虑 ARIA(Accessible Rich Internet Applications)属性,帮助解读网页结构和交互方式

  • aria-hidden:表示是否对辅助技术隐藏该元素
  • role:制定元素的角色
  • aria-labelledby:父节点设置,会链接到对应 id 的元素,方便把分散在多个 DOM,逻辑上属于一个整体的内容打包阅读

CSS

要确保无 css 的情况下,内容可读,顺序有意义

颜色及对比度

WebAIM 的 Color Contrast Checker 之类的工具来检查方案是否有足够对比度

隐藏的内容

position: absolute 好于 visibility: hidden; display: none; ,前者不阻止屏幕阅读器读取被隐藏的内容

JavaScript

结构交给 HTML,尽量不使用 JS 配合 <div> 伪造功能(unobtrusive 原则)

焦点管理

相比较<button><a> 这样自带语义的元素,<div> 不能用.foucs() ,需要配合tabindex="-1" 属性,和.pop:focus-visible {outline: 0}

  1. 场景是现在很常见的单页面里,进行路由切换/ajax 更改内容时,进行焦点重置,让辅助阅读设备重头开始读,方便视障人士
<nav>
  <a href="/">Home</a>
  <a href="/goods">goods</a>
  <a href="/user">user</a>
</nav>
<main>
+   // tabindex="-1",元素可聚焦,不可通过键盘导航访问
+	<h2 tabindex="-1">商城</h2>
    <Link />
</main>
+ <script>
+    function routerChange() {
+        const heading = document.querySelector('h2')
+        heading.focus()
+        document.title = heading.textContent
+   }
+ </script>
  1. Dialog 配合 A11y

需求:弹窗打开时聚焦于弹窗上,限制对页面其他元素的访问,退出弹窗时焦点退回到之前的位置

给弹窗增加 aria 属性

<section 
  id="alert-dialog" 
  role="dialog" 
  aria-modal="true" 
  aria-label="home-dialog" 
  tanindex="-1"
  >
  <h2>TITLE</h2>
  ...
  <button></button>
</section>

转移焦点

function showSelfAlert() {
    // 获取目标元素
    var mask = document.querySelector('.mask');
    // 显示弹窗
    mask.style.display = 'block'; 
    // 等待DOM更新,在下一个事件循环时
    setTimeout(function() {
        // 设置焦点到遮罩层或弹窗的元素上
        var dialog = document.getElementById('aria-mask-dialog');
        if(dialog) {
            dialog.focus();
        }
    }, 0); 
}

<section aria-live="polite"></section>
// aria-live 默认属性为off,
// polite 在系统空闲时朗读
// assertive 立刻阅读

限制焦点

  • 使用 event capturing 监听 focus 事件,处理阶段:捕获-目标-冒泡
document.addEventListener('focus', function(event){
  const dialog = document.getElementById('dialog');
  if(dialogOpen && !dialog.contains(event.target)){
    event.stopPropagation();
    dialog.focus();
  }
})

打开弹窗前记录最后一个焦点元素,结束后把焦点挂回去

// 获取当前聚焦的元素
let lastFouceElement = document.activeElement;
// close dialog
lastFouceElement.focus()

参考&致谢

https://developer.mozilla.org/zh-CN/docs/Learn/Accessibility

Web如何适配无障碍?