首页 > 解决方案 > 用于将按钮作为顶级项目的多级导航菜单的键盘 a11y

问题描述

我有一个<nav>有 2 个级别的元素。顶级<button>项目用标签标记,子项目用<a>标签标记,例如:

<nav role="navigation">
    <ul>
        <li>
            <button type="button">Category 1</button>

            <ul class="submenu">
                <li>
                    <a href="/cat1/subcat1">Subcategory 1</a>
                </li>
                <!-- ... other links -->
            </ul> <!-- /.submenu -->
        </li>
        <li>
            <button type="button">Category 2</button>

            <ul class="submenu">
                <li>
                    <a href="/cat2/subcat1">Subcategory 1</a>
                </li>
                <!-- ... other links -->
            </ul> <!-- /.submenu -->
        </li>
    </ul>
</nav>

顶级项目仅用作容器,没有自己的页面可链接。

我应该如何为此标记添加键盘可访问性?

我目前考虑的选项是:

选项1

当顶级项目(<button>标签)被聚焦时打开菜单。
当用户在子项之间切换时,保持菜单打开。
当焦点移动到另一个顶级项目或页面的其余部分时关闭菜单。
此外,允许通过 Escape 键关闭菜单并转移焦点。

选项 2

使用 arole="menu"将获得的焦点语义,即:
顶级项上的 Enter、Space 或向下箭头打开菜单;
子项上的向下箭头将焦点移至下一项;
子项上的向上箭头将焦点移至前一项;
顶级项目上的向上箭头关闭菜单,并且;
子项上的退出键,关闭菜单并将焦点转移到顶级项。

这将允许保留 Tab 键以移动到下一个顶级项目。

就个人而言,我更喜欢选项 2,但我想知道它是否模糊了预期和显示之间的界限。

标签: javascripthtmlaccessibility

解决方案


选项 2 最初感觉像是正确的键盘语义,但这种行为应该保留给经典意义上的“真正”菜单。也就是说,诸如“文件、编辑、查看...”之类的菜单下列出了即时操作(例如文件 > 打开或编辑 > 首选项)。选项 2 还意味着您的菜单必须是一个制表位,并且用户将使用左/右箭头从“类别 1”移动到“类别 2”。我不认为你想要那个。

对于导航“菜单”,它们并不是经典意义上的真正菜单。它们只是用于导航到其他页面的链接的分组列表。

W3C 上有一个关于菜单的优秀教程 - https://www.w3.org/WAI/tutorials/menus/。(该教程中有一个关于“应用程序菜单”的部分讨论了“经典”菜单,例如“文件、编辑、查看”,如上所述。)

几乎总是建议使用按钮进行即时操作,使用链接导航到另一个页面,但是对于导航菜单,即使它们只控制更多菜单项的下拉列表(通常是按钮的工作),也经常使用链接。是否为顶级项目使用按钮或链接取决于您。就个人而言,我会使用链接,以便链接中的所有元素<nav>都是链接,但这只是个人喜好。

在任何情况下,无论您对顶级项目使用按钮还是链接,该元素aria-expanded最初都应该设置为“false”。当用户选择顶级链接时,更改aria-expanded为“true”。

这是我期望的键盘行为:

  • 我应该能够tab跨越顶级项目。从“类别 1”到“类别 2”的选项卡。(顶级项目的视觉外观应该有一个“向下三角形”或一些图标,表明该菜​​单中有一些东西。)当顶级项目获得焦点时,不要打开子菜单这使得键盘用户很难到达最后一个顶级项目,因为他们必须浏览每个子菜单。
  • 当我浏览顶级项目时,我应该能够点击enter展开下拉菜单。焦点不应移动到第一个子菜单项。它应该保留在顶级项目上。按tab应该带我到第一个子菜单项。(如果您的 DOM 在顶级项目之后立即有子菜单项,您将“免费”获得这种选项卡行为。您的示例代码就是一个很好的例子。)然后我可以通过所有子菜单项进行选项卡。
  • 如果我在子菜单中切换时按下esc,子菜单应该关闭,并且焦点应该移动到打开子菜单的顶级项目(并且aria-expanded应该将顶级项目设置为“false”)。
  • 如果我tab关闭了最后一个子菜单项,子菜单应该关闭并且焦点应该转到下一个顶级项目(并且aria-expanded应该为打开子菜单的顶级项目设置为“false”)。
  • 如果 I shift+ taboff 第一个子菜单项,焦点自然应该转到打开子菜单的顶级项。子菜单仍应保持可见。如果 I shift+tab再次,子菜单应该关闭并且焦点应该转到上一个顶级项目(并且aria-expanded应该为打开子菜单的顶级项目设置为“false”)。

起初听起来有点复杂,但实际上非常简单和优雅。好消息是您将免费获得大部分的标签行为。你只需要实现enter打开子菜单、esc关闭子菜单和一些 onblur() 处理程序来关闭子菜单。

作为旁注,您不必role为已分配默认角色的本机 html 元素指定 a 。例如

<nav role="navigation">

应该只是

<nav>

<button type="button">Category 1</button>

应该只是

<button>Category 1</button>

html5 规范向您展示了默认角色,并特别<nav>说明<button>

(默认 - _不设置_

(此外,您<nav>还应该有一个aria-labelor aria-labelledby。这在上面提到的教程的“标签菜单”部分中提到。)


推荐阅读