键盘a11y,用于多级导航菜单,其中按钮作为顶层项目

时间:2019-01-03 11:32:40

标签: javascript html accessibility

我有一个<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

使用role="menu"将获得的焦点语义,即:
在顶层项目上按Enter,空格或向下箭头打开菜单;
子项上的向下箭头将焦点移至下一项;
子项上的向上箭头将焦点移至上一项;
顶级项目上的向上箭头关闭菜单,并且;
在子项上时,按Esc键可以关闭菜单,并将焦点转移到顶级项上。

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

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

1 个答案:

答案 0 :(得分:1)

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

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

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

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

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

这是我期望的键盘行为:

  • 我应该能够在所有顶级项目上 tab 。从“类别1”选项卡切换到“类别2”。 (顶级项目的视觉外观应带有“下三角”或表示该菜单中包含某些内容的图标。)当顶级项目获得焦点时,请打开子菜单。这使得键盘用户很难进入最后一个顶级项目,因为他们必须浏览每个子菜单。
  • 当我在顶级项目上切换时,我应该可以点击 enter 展开下拉菜单。焦点不应移到第一个子菜单项。它应保留在顶层项目上。按下 tab 会将我带到第一个子菜单项。 (如果您的DOM在顶级项目之后紧跟着子菜单项,您将获得“免费”这种制表行为。您的示例代码就是一个很好的例子。)然后,我可以在所有子菜单项中进行制表。
  • 如果在选择子菜单时按 esc ,则子菜单应关闭,焦点应移至打开子菜单的顶层项目(并且aria-expanded应设置为顶级项目为“ false”。
  • 如果我 tab 离开了最后一个子菜单项,则该子菜单应关闭并且焦点应移至下一个顶级项(并且aria-expanded的设置应为“ false”打开子菜单的顶级项)。
  • 如果我 + 选项卡从第一个子菜单项移开,则焦点自然应该转到打开该子菜单的顶级项。子菜单仍应保持可见。如果我再次 shift + tab ,请然后关闭子菜单,并将焦点移至上一个顶级项目(和{{1} }应该将打开子菜单的顶级项目设置为“ false”。

乍一看听起来有些复杂,但实际上相当简单而优雅。令人高兴的是,您将免费获得大多数制表行为。您只需要实现 enter 来打开子菜单,实现 esc 来关闭子菜单,并使用一些onblur()处理函数来关闭子菜单。

作为旁注,您不必为已经分配了默认角色的本机html元素指定aria-expanded。例如

role

应该是

<nav role="navigation">

<nav>

应该是

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

<nav><button>的html5规范向您显示了默认角色,并特别指出

  

(默认- _不设置_

(此外,您的<button>Category 1</button> 还应该有一个aria-labelaria-labelledby。在上述教程的“ Label menus”部分中对此进行了提及。)