为了账号安全,请及时绑定邮箱和手机立即绑定

HTML 辅助功能选项卡无法按预期工作

HTML 辅助功能选项卡无法按预期工作

偶然的你 2023-09-25 16:06:38
我有一个包含 3 个input元素的表单。可访问性团队需要将元素集中在 上tab press。效果很好。活动也表现出色shift+tab。但问题是在焦点到达提交按钮后,他们希望从第一个输入元素继续,而不是离开页面并聚焦地址栏。是否可以?我怎样才能让焦点循环我的表格tab而shif+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> 此弹出窗口是在页面加载时出现还是使用页面上的按钮激活?=>单击按钮显示弹出窗口(用于确认)弹出窗口位于 DOM 中的什么位置 - 是在 / 等中还是在文档流之外?=>在 dom 内(放置为角度分量)2a. 如果它在 / 等之内,您是否可以将其移到该之外。=>它已经坐在外面了。因为弹出页面基于他们想要一个完全可访问的版本还是选项卡是唯一的要求(因为屏幕阅读器用户不倾向于使用选项卡进行导航,而是使用标题、链接、表单模式等的快捷方式)。抱歉,很多问题只需要知道如何深入回答即可。=>需要完全可访问
查看完整描述

1 回答

?
FFIVE

TA贡献1797条经验 获得超6个赞

感谢您回答这些问题,希望以下解释能够突出我问他们的原因(然后我将提供一些解决方案)。

为什么我们不能直接拦截tab密钥呢?

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

这会导致弹出窗口的可访问性问题,因为人们只倾向于捕获密钥tab。然后,如果用户使用快捷方式(例如2在 NVDA 中)跳转页面上的第 2 级标题,他们最终可能会在不知道模态存在的情况下跳到模态之外,而且通常没有任何方法可以在不进行 Tab 切换很长时间的情况下返回到模态。

所以解决方案很明显,确保页面上的其他内容都不可访问(而不仅仅是不可聚焦)。

然而,您需要对 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"确保使用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(); 就会调用该行将焦点设置到模式中的第一个可聚焦项目,您不需要在第一个元素打开时按 Tab 键,它应该自动聚焦。


点 5 被线覆盖,kluio.globalVars.beforeOpen = item;以设置对打开模式的项目的引用,并kluio.globalVars.beforeOpen.focus();在关闭函数内将焦点返回到该项目。


第 6 点包含在document.onkeydown从 开始的函数中if (kluio.globalVars.modalOpen && evt.keyCode == 9) {。


我希望以上所有内容都清楚,有任何问题尽管问,如果以后有时间我会把它变成一个小提琴。


查看完整回答
反对 回复 2023-09-25
  • 1 回答
  • 0 关注
  • 73 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信