HTML辅助功能选项卡无法正常工作

时间:2020-03-19 07:35:46

标签: html accessibility

我有一个带有3个input元素的表格。无障碍团队需要将元素集中在tab press上。它运作良好。 shift+tab的活动也做得很好。但是问题在于焦点到达“提交”按钮之后,他们希望从第一个输入元素继续而不是离开页面并聚焦地址栏。

有可能吗?我该如何集中精力在tabshif+tab中循环我的表单,而不是搬出去?

我正在以popup模式显示表单。

<form action="/action_page.php">
  <label for="fname">First name:</label><br>
  <input tabindex="1" type="text" id="fname" name="fname" value="John"><br>
  <label for="lname">Last name:</label><br>
  <input tabindex="2" type="text" id="lname" name="lname" value="Doe"><br><br>
  <input tabindex="3" type="submit" value="Submit">
</form> 

  1. 此弹出窗口是在页面加载时显示还是使用页面上的按钮激活的? => 按钮单击以显示弹出窗口(以确认)
  2. 弹出窗口在DOM中位于何处-是在/ etc中还是在文档流之外? => 在dom内(放置为角度分量)

2a。如果它在/等之内,则可以将其移到该之外。 => 它已经坐在外面了。因为弹出页面基于

  1. 他们是否需要完全可访问的版本,还是仅使用制表符(因为屏幕阅读器用户不倾向于使用制表符进行导航,而是使用标题,链接,表单模式等的快捷方式)。抱歉,许多问题只需要知道答案的深度即可。 => 需要完全访问权限

1 个答案:

答案 0 :(得分:1)

感谢您回答问题,希望下面的解释可以突出我为什么要问他们(然后我将提供一些解决方案)。

为什么我们不能仅截获 tab 键?

屏幕阅读器用户不能仅使用Tab键进行导航。根据他们使用的屏幕阅读器,他们使用不同的快捷方式来浏览标题,链接,表单等。

这会导致弹出窗口的可访问性问题,因为人们只倾向于捕获 tab 键。然后,如果用户使用快捷方式,例如NVDA中的 2 可以跳过页面上的标题级别2s,他们可能不知道模态而最终退出模态,通常无法以任何方式返回模态而无需花费很多时间。

因此,解决方案很明显,请确保页面上的其他所有内容均不可访问(不仅仅是无法聚焦)。

但是,您需要使DOM结构井井有条/井井有条,以使其易于管理。

要解决的问题

  1. 屏幕阅读器用户可以访问不可聚焦的元素
  2. 他们可以更改其快捷键,因此我们不能依靠拦截按键来尝试解决问题。
  3. 我们希望保持相同的视觉设计(即我们不能仅在所有其他元素上使用display:none)。
  4. 我们希望我们可以重复一种模式,这样我们就不能只在页面上单独隐藏元素。
  5. 我们希望正确地管理焦点,以便在模式关闭时将焦点恢复到上一个​​项目(根据您的情况)。
  6. 我们想在到达最后一个项目时返回模态中的第一个项目(我们可以拦截 tab 键来完成此操作,因为我们无法涵盖所有​​情况,我们也不想那样会导致更多的可访问性问题。)

解决方案

问题1、2、3和4

由于我们无法拦截按键来管理模式中的焦点,因此必须在模式处于活动状态时使其他所有元素(模式中的元素除外)完全不可访问。

对于屏幕阅读器,

aria-hidden="true"实际上是display: none。所有屏幕阅读器/浏览器组合对aria-hidden的支持大约占90%到95%。

要使模式外部的内容不可访问,我们需要向模式外部的每个元素添加aria-hidden="true"以及tabindex="-1",以确保使用< kbd> tab 键。

我询问您的文档结构,因为最简单的方法是在区域/主要地标上。

因此,当模态处于活动状态时,我们需要将aria-hidden="true"tabindex="-1"添加到<head><main><footer>等中。具有里程碑意义的级别,并且通过将模式放在主文档流之外,可以在保留语义HTML标记的同时轻松进行管理和维护。模态的情况恰恰相反(因此,在不使用该模态时,请使用相同的技术将其隐藏。)

模式打开前

<head aria-hidden="false"></head>
<main aria-hidden="false"></main>
<footer aria-hidden="false"></footer>
<div class="modal" aria-hidden="true" tabindex="-1"></div>

模式打开

<head aria-hidden="true" tabindex="-1"></head>
<main aria-hidden="true" tabindex="-1"></main>
<footer aria-hidden="true" tabindex="-1"></footer>
<div class="modal" aria-hidden="false"></div>

请注意,我总是如何添加aria-hidden,因为某些屏幕阅读器对动态添加aria的反应不佳(尽管它们对更改属性的反应很好)。

第5点和第6点

为此,我认为最简单的方法就是共享用于将焦点捕获到模式中的代码。

以下功能的目的是在模式中的第一个可聚焦项在打开时聚焦,存储对激活模式的元素的引用(因为我们想在模式关闭时将用户返回该模式)并进行管理重点。

请注意,我使用一个微型库来启用jQuery样式选择器,因此您可能需要进行一些调整以供使用。

管理焦点说明和代码

item变量是在打开模式之前已按下的引用按钮(因此我们可以在关闭模式后返回焦点)。

className变量是模态的类名,因此您可以定位不同的模态。

kluio.helpers只是我在网站上使用的一系列功能,因此可以省略。

kluio.globalVars是一个全局变量数组,因此可以用来代替从函数返回结果。

我在每个部分都添加了注释,以解释其作用。

打开模态时,将调用setFocus函数,传入被按下以激活它的元素和模态的className(对于我们的用例更好,您可以使用ID代替)

var kluio = {};
kluio.helpers = {};
kluio.globalVars = {};

kluio.helpers.setFocus = function (item, className) { //we pass in the button that activated the modal and the className of the modal, your modal must have a unique className for this to work.

    className = className || "content"; //defaults to class 'content' in case of error ("content" being the class on the <main> element.)
    kluio.globalVars.beforeOpen = item; //we store the button that was pressed before the modal opened in a global variable so we can return focus to it on modal close.

    var focusableItems = ['a[href]', 'area[href]', 'input:not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])', 'button:not([disabled])', '[tabindex="0"]']; //a list of items that should be focusable.
    var findItems = [];
    for (i = 0, len = focusableItems.length; i < len; i++) {
        findItems.push('.' + className + " " + focusableItems[i]); //add every focusable item to an array.
    }

    var findString = findItems.join(", ");
    kluio.globalVars.canFocus = Array.prototype.slice.call($('body').find(findString)); //please note we use a custom replacement for jQuery, pretty sure .find() behaves identically but just check it yourself.
    if (kluio.globalVars.canFocus.length > 0) {
        setTimeout(function () { //set timeout not needed most of the time, we have a modal that is off-screen and slides in, setting focus too early results in the page jumping so we added a delay, you may be able to omit this.
            kluio.globalVars.canFocus[0].focus(); //***set the focus to the first focusable element within the modal
            kluio.globalVars.lastItem = kluio.globalVars.canFocus[kluio.globalVars.canFocus.length - 1]; //we also store the last focusable item within the modal so we can keep focus within the modal. 
        }, 600);
    }
}

然后,我们使用以下函数来拦截keydown事件以管理焦点。

document.onkeydown = function (evt) {
    evt = evt || window.event;
    if (evt.keyCode == 27) {
        closeAllModals(); //a function that will close any open modal with the Escape key
    }
    if (kluio.globalVars.modalOpen && evt.keyCode == 9) { //global variable to check any modal is open and key is the tab key
        if (evt.shiftKey) { //also pressing shift key
            if (document.activeElement == kluio.globalVars.canFocus[0]) { //the current element is the same as the first focusable element
                evt.preventDefault();
                kluio.globalVars.lastItem.focus(); //we focus the last focusable element as we are reverse tabbing through the items.
            }
        } else {
            if (document.activeElement == kluio.globalVars.lastItem) { //when tabbing forward we look for the last tabbable element 
                evt.preventDefault();
                kluio.globalVars.canFocus[0].focus(); //move the focus to the first tabbable element.
            }
        }
    }
};

最后,在您的closeAllModals函数版本中,您需要将焦点返回到引用元素/按钮。

if (kluio.globalVars.beforeOpen) {
    kluio.globalVars.beforeOpen.focus();
}

kluio.globalVars.canFocus[0].focus(); 行被调用以将焦点设置为模式中的第一个可聚焦项,一旦激活,您无需在打开第一个元素时将其切换为第一个可自动聚焦的项。

第5行由kluio.globalVars.beforeOpen = item;行覆盖,以设置对打开模式的项目的引用,并在close函数中设置对kluio.globalVars.beforeOpen.focus();的引用,以将焦点返回到该项目。

document.onkeydown开始的if (kluio.globalVars.modalOpen && evt.keyCode == 9) {函数中涵盖了第6点。

我希望以上所有内容都很清楚,任何问题都可以问,如果我以后有空,我会把所有这些变成小提琴。