焦点在可见区域之外
Focus outside of visible area
WCAG 声明具有焦点的元素应该始终对用户可见。这在 space 受限的聊天 window 中即使不是不可能,也是特别困难的。
当键盘或屏幕 reader 用户切换到第一个按钮选项并选择它时,内容滚动并且按钮不再可见,这违反了 "focus always visible" WCAG 规则。此外,如果列表中有多个按钮,焦点将停留在按钮上,如果它们继续切换,window 将滚动到设置焦点的位置。这是令人迷惑的,人们可以说,选择按钮时,其他选项无关紧要,因为现在可以使用新选项。
示例:https://recordit.co/2jDDvqg98J
一个选项是在到达按钮时停止滚动,以便按钮可见。但我觉得这不是一个很好的体验,是遵守WCAG规则的妥协。我做了一些研究,所有会话 UI,无一例外,在聊天中打印新内容时滚动到底部。如果我以上述方式偏离以保持在 WCAG 范围内,我将打破 Jakobs Law.
另一个选项是将焦点从所选按钮移到输入字段或 next/new 可用按钮列表中的第一个按钮。但是我觉得这个针对盲人用户的遗嘱删除了所有参考点。
您是否可以想到任何其他选项或设计来以易于访问的方式解决此问题?
不是完整答案
这个问题太大,无法完整回答,有成百上千的事情需要考虑,仅通过一张 GIF 来查看我只能概括的行为。
屏幕外对焦
你把 WCAG 带到了绝对的信中,有点过头了。这一点的目的是当我 更改 焦点时,焦点项目在屏幕上可见。
如果我滚动一个网页,你不能指望焦点项目停留在页面上(否则,如果它的长度超过屏幕长度,世界上没有任何页面是兼容的!)。
只要当我聚焦下一个项目时它会滚动到视图中并且有一个我可以看到的聚焦指示器(正确的对比度,不依赖于颜色)你在这一点上是 WCAG AAA 评级!
自动滚动内容
事情就是这样 'murky'。在您的示例中,内容被添加到聊天框,因此当前聚焦的项目滚出屏幕。
现在,如果我们将焦点移至最新选项(出现的按钮),我们会遇到一个问题,它可能会中断屏幕的流动 reader,因此他们听不到之前的所有信息,并且得回去再听一遍
如果我们不移动焦点,你就会遇到你描述的问题,我按下 Tab(例如),页面跳回到我刚听的内容之前.
假设我们没有任何其他选择,这仍然是更可取的(不是管理焦点),但我们确实有选择。
是时候采取一些变通办法了。
这是辅助功能变得有趣的地方,我们需要找到一些解决方法,因为这里没有既定的模式。
我认为这里最大的问题是,一旦说出新文本,我们就会在按 Tab 或其他焦点快捷方式时跳回页面顶部。
解决此问题的一个解决方法是创建一个特殊的 div,它在我选择一个选项的第二个时间点得到关注。
这个 div 会 tabindex="-1"
所以它不能通过键盘访问(只能通过编程)。
第二个我select一个选项我们关注这个div然后开始插入文本。
这样当我按下 <kbd>Tab</kbd>
或下一个按钮的快捷方式时,它会跳转到第一个新选项。
我在下面创建了一个基本的 fiddle,它需要一些改进,但为您提供了一个测试/使用的模式。 (全屏显示,否则您可能看不到添加的按钮等)
var hasLoadedMore = 0;
$('.loadMore').on('click', function(e){
if(hasLoadedMore == 0){ //just a horrible hack to simluate content only loading once when you click an option.
console.log("option chosen");
$('#focusAdjuster').attr('aria-hidden', false);
$('#focusAdjuster').focus();
console.log("focus adjusted");
loadContent();
}
});
function loadContent(){
///ugly way of simulating the content being added dynamically.
$('#chat').append('<p>additional text</p>');
$('#chat').append('<p>more text</p>');
setTimeout(function(){
$('#chat').append('<p>more text</p>');
$('#chat').append('<button>Option 1 new</button>');
$('#chat').append('<button>Option 2 new</button>');
}, 500);
hasLoadedMore = 1;
}
.visually-hidden {
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
clip: rect(1px, 1px, 1px, 1px);
white-space: nowrap; /* added line */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="chat">
<p>initial text</p>
<button class="loadMore">Option 1</button>
<button class="loadMore">Option 2</button>
<button class="loadMore">Option 3</button>
<div tabindex="-1" aria-hidden="true" id="focusAdjuster" class="visually-hidden">loading</div>
</div>
fiddle
的解释
所以他们的关键点是 visually-hidden <div>
和 JavaScript 中的伴随行,重点是 div before 加载更多内容。
div 让我们可以在单击按钮后更改焦点。我还在 div 内的文本中添加了 'loading' 以添加额外的用途,因为您的应用程序将 AJAX 供电。
div 有 tabindex="-1"
,因此无法获得焦点。我还为此添加了 aria-hidden="true"
并在单击选项按钮时将其关闭,就在给予它焦点之前。
当焦点在现实世界中离开这个 div 时,我会切换回来,但我在快速演示中没有这样做 fiddle。
这不是 'fail' WCAG 吗?
是啊!在此示例中,我将 non-focusable 项设置为可聚焦,但它没有任何操作。我仍然认为这比使它成为 <button>
更可取,因为这意味着它有一个动作。它显然并不完美。
然而,WCAG 的关键部分是“G”——它们是 G 指南。我建议的方式是 'hack' 或基于我对开发时间和技术限制的现实态度的妥协。
在没有 div 的情况下,'proper' 的方法是进行一些仔细的焦点管理。没有时间和预算,我一定会这样做。
但考虑到您不必只考虑带有屏幕 reader 的 Tab 键(您可以通过链接、按钮、标题、部分进行导航等等),这在试图拦截击键时变成了一场噩梦,所以上面是我能想到的最简单的方法。
替代模式。
因为前面的例子 'fails' WCAG 还有另一种选择,但是我认为这更糟并且会引入很多问题。
每次用新文本替换 div 中的文本。
不利的一面是您每次都需要提供 'previous item' 按钮并跟踪它们,有利的一面是这将符合 WCAG(尽管我认为即使您 'pass' 标准。)
同时提供那些 'previous' 按钮再次引入了焦点管理的一些问题(我们什么时候让它们可见,它们是否获得焦点,它们是否增加了更多的混乱?)。
把它想象成一个表单向导模式,每组问题都是一个 'step',所以你可以回到上一个步骤,屏幕上只能看到当前步骤。
我把它包括在内是因为这个模式可以通过一些想法扩展并给 OP/其他人一些想法,不要按原样使用它
var hasLoadedMore = 0;
var optionChosen = "";
$('.loadMore').on('click', function(e){
if(hasLoadedMore == 0){ //just a horrible hack to simluate content only loading once when you click an option.
optionChosen = $(this).text();
console.log("option chosen", optionChosen);
loadContent();
}
});
function loadContent(){
$('#chat').html('<button class="previousQuestion">You chose ' + optionChosen + '<span class="visually-hidden">(click here to go back and chose a different option)</span></button>');
$('#chat').append('<h3>' + optionChosen + '</h3>');
///ugly way of simulating the content being added dynamically.
$('#chat').append('<p>additional text</p>');
$('#chat').append('<p>more text</p>');
setTimeout(function(){
$('#chat').append('<p>more text</p>');
$('#chat').append('<button>Option 1 new</button>');
$('#chat').append('<button>Option 2 new</button>');
}, 500);
hasLoadedMore = 1;
$('.previousQuestion').on('click', function(){
console.log("now you would restore the previous question");
});
}
.visually-hidden {
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
clip: rect(1px, 1px, 1px, 1px);
white-space: nowrap; /* added line */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="chat">
<p>initial text</p>
<button class="loadMore">Option 1</button>
<button class="loadMore">Option 2</button>
<button class="loadMore">Option 3</button>
<div tabindex="-1" aria-hidden="true" id="focusAdjuster" class="visually-hidden">loading</div>
</div>
你的例子有点太复杂了。
让我们来看一个标准的 HTML 页面:如果您使用键盘聚焦某个元素,您仍然可以使用鼠标滚动页面(无需修改键盘光标),并使该元素从视口。这不会使任何 WCAG 标准无效。
在 textarea
上按一个字符键将使其再次在视觉上聚焦。对于按钮或 link,需要 tab 然后 shifttab,到使按钮可见并再次聚焦。
视觉焦点和键盘焦点是两个截然不同的事物,但如果键盘焦点影响视觉焦点,则不会相互影响
你的问题可能不是对焦的问题,更多的是Time limits的问题。
如果用户不在 window、
底部则不滚动
如果在最后 N 秒内执行了某个操作,则不滚动,或者如果在滚动之前没有执行其他操作,则等待 P 秒。
如果windows没有滚动,表示已经收到新消息。
我认为 Slack 很好地实现了 "do not scroll and indicate new messages" 如果你不在底部。
WCAG 声明具有焦点的元素应该始终对用户可见。这在 space 受限的聊天 window 中即使不是不可能,也是特别困难的。
当键盘或屏幕 reader 用户切换到第一个按钮选项并选择它时,内容滚动并且按钮不再可见,这违反了 "focus always visible" WCAG 规则。此外,如果列表中有多个按钮,焦点将停留在按钮上,如果它们继续切换,window 将滚动到设置焦点的位置。这是令人迷惑的,人们可以说,选择按钮时,其他选项无关紧要,因为现在可以使用新选项。
示例:https://recordit.co/2jDDvqg98J
一个选项是在到达按钮时停止滚动,以便按钮可见。但我觉得这不是一个很好的体验,是遵守WCAG规则的妥协。我做了一些研究,所有会话 UI,无一例外,在聊天中打印新内容时滚动到底部。如果我以上述方式偏离以保持在 WCAG 范围内,我将打破 Jakobs Law.
另一个选项是将焦点从所选按钮移到输入字段或 next/new 可用按钮列表中的第一个按钮。但是我觉得这个针对盲人用户的遗嘱删除了所有参考点。
您是否可以想到任何其他选项或设计来以易于访问的方式解决此问题?
不是完整答案
这个问题太大,无法完整回答,有成百上千的事情需要考虑,仅通过一张 GIF 来查看我只能概括的行为。
屏幕外对焦
你把 WCAG 带到了绝对的信中,有点过头了。这一点的目的是当我 更改 焦点时,焦点项目在屏幕上可见。
如果我滚动一个网页,你不能指望焦点项目停留在页面上(否则,如果它的长度超过屏幕长度,世界上没有任何页面是兼容的!)。
只要当我聚焦下一个项目时它会滚动到视图中并且有一个我可以看到的聚焦指示器(正确的对比度,不依赖于颜色)你在这一点上是 WCAG AAA 评级!
自动滚动内容
事情就是这样 'murky'。在您的示例中,内容被添加到聊天框,因此当前聚焦的项目滚出屏幕。
现在,如果我们将焦点移至最新选项(出现的按钮),我们会遇到一个问题,它可能会中断屏幕的流动 reader,因此他们听不到之前的所有信息,并且得回去再听一遍
如果我们不移动焦点,你就会遇到你描述的问题,我按下 Tab(例如),页面跳回到我刚听的内容之前.
假设我们没有任何其他选择,这仍然是更可取的(不是管理焦点),但我们确实有选择。
是时候采取一些变通办法了。
这是辅助功能变得有趣的地方,我们需要找到一些解决方法,因为这里没有既定的模式。
我认为这里最大的问题是,一旦说出新文本,我们就会在按 Tab 或其他焦点快捷方式时跳回页面顶部。
解决此问题的一个解决方法是创建一个特殊的 div,它在我选择一个选项的第二个时间点得到关注。
这个 div 会 tabindex="-1"
所以它不能通过键盘访问(只能通过编程)。
第二个我select一个选项我们关注这个div然后开始插入文本。
这样当我按下 <kbd>Tab</kbd>
或下一个按钮的快捷方式时,它会跳转到第一个新选项。
我在下面创建了一个基本的 fiddle,它需要一些改进,但为您提供了一个测试/使用的模式。 (全屏显示,否则您可能看不到添加的按钮等)
var hasLoadedMore = 0;
$('.loadMore').on('click', function(e){
if(hasLoadedMore == 0){ //just a horrible hack to simluate content only loading once when you click an option.
console.log("option chosen");
$('#focusAdjuster').attr('aria-hidden', false);
$('#focusAdjuster').focus();
console.log("focus adjusted");
loadContent();
}
});
function loadContent(){
///ugly way of simulating the content being added dynamically.
$('#chat').append('<p>additional text</p>');
$('#chat').append('<p>more text</p>');
setTimeout(function(){
$('#chat').append('<p>more text</p>');
$('#chat').append('<button>Option 1 new</button>');
$('#chat').append('<button>Option 2 new</button>');
}, 500);
hasLoadedMore = 1;
}
.visually-hidden {
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
clip: rect(1px, 1px, 1px, 1px);
white-space: nowrap; /* added line */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="chat">
<p>initial text</p>
<button class="loadMore">Option 1</button>
<button class="loadMore">Option 2</button>
<button class="loadMore">Option 3</button>
<div tabindex="-1" aria-hidden="true" id="focusAdjuster" class="visually-hidden">loading</div>
</div>
fiddle
的解释所以他们的关键点是 visually-hidden <div>
和 JavaScript 中的伴随行,重点是 div before 加载更多内容。
div 让我们可以在单击按钮后更改焦点。我还在 div 内的文本中添加了 'loading' 以添加额外的用途,因为您的应用程序将 AJAX 供电。
div 有 tabindex="-1"
,因此无法获得焦点。我还为此添加了 aria-hidden="true"
并在单击选项按钮时将其关闭,就在给予它焦点之前。
当焦点在现实世界中离开这个 div 时,我会切换回来,但我在快速演示中没有这样做 fiddle。
这不是 'fail' WCAG 吗?是啊!在此示例中,我将 non-focusable 项设置为可聚焦,但它没有任何操作。我仍然认为这比使它成为 <button>
更可取,因为这意味着它有一个动作。它显然并不完美。
然而,WCAG 的关键部分是“G”——它们是 G 指南。我建议的方式是 'hack' 或基于我对开发时间和技术限制的现实态度的妥协。
在没有 div 的情况下,'proper' 的方法是进行一些仔细的焦点管理。没有时间和预算,我一定会这样做。
但考虑到您不必只考虑带有屏幕 reader 的 Tab 键(您可以通过链接、按钮、标题、部分进行导航等等),这在试图拦截击键时变成了一场噩梦,所以上面是我能想到的最简单的方法。
替代模式。
因为前面的例子 'fails' WCAG 还有另一种选择,但是我认为这更糟并且会引入很多问题。
每次用新文本替换 div 中的文本。
不利的一面是您每次都需要提供 'previous item' 按钮并跟踪它们,有利的一面是这将符合 WCAG(尽管我认为即使您 'pass' 标准。)
同时提供那些 'previous' 按钮再次引入了焦点管理的一些问题(我们什么时候让它们可见,它们是否获得焦点,它们是否增加了更多的混乱?)。
把它想象成一个表单向导模式,每组问题都是一个 'step',所以你可以回到上一个步骤,屏幕上只能看到当前步骤。
我把它包括在内是因为这个模式可以通过一些想法扩展并给 OP/其他人一些想法,不要按原样使用它
var hasLoadedMore = 0;
var optionChosen = "";
$('.loadMore').on('click', function(e){
if(hasLoadedMore == 0){ //just a horrible hack to simluate content only loading once when you click an option.
optionChosen = $(this).text();
console.log("option chosen", optionChosen);
loadContent();
}
});
function loadContent(){
$('#chat').html('<button class="previousQuestion">You chose ' + optionChosen + '<span class="visually-hidden">(click here to go back and chose a different option)</span></button>');
$('#chat').append('<h3>' + optionChosen + '</h3>');
///ugly way of simulating the content being added dynamically.
$('#chat').append('<p>additional text</p>');
$('#chat').append('<p>more text</p>');
setTimeout(function(){
$('#chat').append('<p>more text</p>');
$('#chat').append('<button>Option 1 new</button>');
$('#chat').append('<button>Option 2 new</button>');
}, 500);
hasLoadedMore = 1;
$('.previousQuestion').on('click', function(){
console.log("now you would restore the previous question");
});
}
.visually-hidden {
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
clip: rect(1px, 1px, 1px, 1px);
white-space: nowrap; /* added line */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="chat">
<p>initial text</p>
<button class="loadMore">Option 1</button>
<button class="loadMore">Option 2</button>
<button class="loadMore">Option 3</button>
<div tabindex="-1" aria-hidden="true" id="focusAdjuster" class="visually-hidden">loading</div>
</div>
你的例子有点太复杂了。
让我们来看一个标准的 HTML 页面:如果您使用键盘聚焦某个元素,您仍然可以使用鼠标滚动页面(无需修改键盘光标),并使该元素从视口。这不会使任何 WCAG 标准无效。
在 textarea
上按一个字符键将使其再次在视觉上聚焦。对于按钮或 link,需要 tab 然后 shifttab,到使按钮可见并再次聚焦。
视觉焦点和键盘焦点是两个截然不同的事物,但如果键盘焦点影响视觉焦点,则不会相互影响
你的问题可能不是对焦的问题,更多的是Time limits的问题。
如果用户不在 window、
底部则不滚动
如果在最后 N 秒内执行了某个操作,则不滚动,或者如果在滚动之前没有执行其他操作,则等待 P 秒。
如果windows没有滚动,表示已经收到新消息。
我认为 Slack 很好地实现了 "do not scroll and indicate new messages" 如果你不在底部。