HTML辅助功能选项卡不能按预期方式工作 [英] HTML Accessibility tab not works as expected

查看:143
本文介绍了HTML辅助功能选项卡不能按预期方式工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

解决方案

感谢您回答问题,希望下面的解释将重点介绍我为什么要问他们(然后我将提供一些解决方案).

为什么我们不能仅截获 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>键.

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

因此,当模态处于活动状态时,我们需要向<head><main><footer>等添加aria-hidden="true"tabindex="-1".主文档流,这在保留语义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的反应不佳(尽管它们对更改属性的反应很好),所以我总是添加aria-hidden的方式.

第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();设置引用,以将焦点返回到该项目.

if (kluio.globalVars.modalOpen && evt.keyCode == 9) {开始,document.onkeydown功能涵盖了

第6点.

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

I have a form with 3 input element. the accessibility team requires to focus the element on tab press. it works well. event doing well by shift+tab as well. but the issue is after the submit button reached by focus they want to continue from first input element instead of leaving the page and focusing the address bar.

Is it possible? how can i make focus to loop my form in tab and shif+tab instead of moving out?

I am showing the form in popup modal.

<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. Does this popup appear on page load or is it activated using a button on the page? => button click show the popup( for confirm )
  2. Where is the popup located within the DOM - is it within a / etc. or is it outside of the document flow? => within dom (placed as angular component )

2a. If it is within the / etc. are you able to move it outside of that. => it's sitting outside out it already. because the popup page based

  1. Do they want a fully accessible version or is tab the only requirement (as screen reader users do not tend to use tab to navigate, instead using shortcuts for headings, links, forms mode etc.). Sorry lots of questions just need to know how in-depth to go in answer. => required as fully accessible

解决方案

Thanks for answering the questions, hopefully the following explanation will highlight why I was asking them (and then I will offer some solutions).

Why can't we just intercept the tab key?

Screen reader users don't navigate using only the tab key. Depending on the screen reader they use they use different shortcuts to navigate via headings, links, forms etc.

This causes accessibility issues with popups as people only tend to capture the tab key. Then if a user uses a shortcut, e.g. 2 in NVDA to jump through heading level 2s on the page they can end up outside of the modal without knowing it exists, often without any way to get back into the modal without tabbing around for ages.

So the solution is obvious, make sure nothing else on the page is accessible (not just not focusable).

However you need to have your DOM structure well ordered / organised to make this manageable.

Problems to solve

  1. Screen reader users can access non-focusable elements
  2. They could change their shortcut keys so we can't rely on intercepting key presses to try and fix the problem.
  3. We want to maintain the same visual design (i.e. we can't just use display:none on all other elements).
  4. We want a pattern we can repeat so we can't just individually hide elements on the page.
  5. We want to manage focus correctly so that when the modal is closed it reverts focus back to the previous item (in your circumstances).
  6. We want to loop back to the first item in the modal upon reaching the last item (we can do this intercepting the tab key as we can't cover all scenarios, neither do we want to as that would cause more accessibility issues.)

Solution

problems 1, 2, 3 and 4

As we cannot intercept key presses to manage focus within the modal we have to make every other element (other than those in the modal) completely inaccessible while the modal is active.

aria-hidden="true" is effectively display: none for screen readers. Support for aria-hidden is good at around 90% to 95% for all screen reader / browser combos.

To make the content outside of the modal inaccessible we need to add aria-hidden="true" to every element outside of the modal as well as tabindex="-1" to ensure that nothing can be focused outside of the modal using the tab key.

I asked about your document structure as the easiest way to implement this is on regions / main landmarks.

So when the modal is active we need to add aria-hidden="true" and tabindex="-1" to the <head>, <main>, <footer> etc. By doing it at the landmark level and by putting the modal outside of the main document flow this becomes easy to manage and maintain while preserving semantic HTML markup. The opposite is true of the modal (so hide it using the same technique when it isn't active.)

Before modal open

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

Modal open

<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>

Note how I have aria-hidden always added as some screen readers do not react well to dynamic addition of aria (they react fine to changing properties though).

Points 5 and 6

For this I think it will be easiest to share the code I use to trap focus within a modal.

The purpose of the below function is to focus the first focusable item within a modal when it opens, store a reference to the element that activated the modal (as we want to return the user there when the modal closes) and to manage focus.

Please note that I use a micro library to enable jQuery style selectors so you may need to tweak things for your use.

Managing focus explanation and code

The item variable is the referring button that was pressed before opening the modal (so we can return focus there after closing the modal).

The className variable is the class name of the modal so you can target different modals.

kluio.helpers is just an array of functions I use across the site so can be omitted.

kluio.globalVars is an array of global variables so could be substituted for returning the results from the function.

I have added comments to each part to explain what it does.

The setFocus function is called when the modal is opened passing in the element that was pressed to activate it and the modal's className (works for our use case better, you could use an ID instead).

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);
    }
}

We then intercept the keydown event with the following function to manage focus.

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.
            }
        }
    }
};

Finally in your version of the closeAllModals function you need to return focus to the referring element / button.

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

The line kluio.globalVars.canFocus[0].focus(); is called to set focus to the first focusable item within the modal once it is activated, you shouldn't need to tab into the first element when it opens it should be automatically focused.

Point 5 is covered by the line kluio.globalVars.beforeOpen = item; to set a reference to the item that opened the modal and kluio.globalVars.beforeOpen.focus(); within the close function to return focus to that item.

Point 6 is covered within the document.onkeydown function starting at if (kluio.globalVars.modalOpen && evt.keyCode == 9) {.

I hope all of the above is clear, any questions just ask, if I have time later I will turn it all into a fiddle.

这篇关于HTML辅助功能选项卡不能按预期方式工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆