不是完整的答案
这是一个太大的问题,无法完整回答,有数百件事情需要考虑,只有一个 GIF 才能看到我只能概括的行为。
离屏对焦
你已经把 WCAG 带到了绝对的字母上,而且有点过分了。这一点的目的是当我改变焦点时,焦点项目在屏幕上可见。
如果我滚动一个网页,你不能期望焦点项目留在页面上(否则,如果它比屏幕长度长,世界上没有任何页面是合规的!)。
只要当我关注下一个项目时,它就会滚动到视图中并有一个焦点指示器,我可以看到(正确的对比度,不依赖于颜色)你在这一点上被评为 WCAG AAA!
自动滚动内容
这就是事情变得“模糊”的地方。在您的示例中,内容被添加到聊天框中,因此当前关注的项目会滚动到屏幕外。
现在,如果我们将焦点移至最新选项(出现的按钮),我们会遇到一个问题,即它可能会中断屏幕阅读器的流程,因此他们无法听到所有先前的信息,而不得不返回并再次收听。
如果我们不移动焦点,您会遇到您描述的问题Tab(例如),并且页面会在我刚刚收听的内容之前跳回。
假设我们没有任何其他选择,这仍然是可取的(不是管理焦点),但我们确实有选择。
是时候采取一些解决方法了。
这是可访问性变得有趣的地方,我们需要找到一些解决方法,因为这里没有既定的模式。
我认为这里最大的问题是,一旦说出新文本,当我们按下Tab或其他焦点快捷键时,我们会跳回页面顶部。
一种解决方法是创建一个特殊的 div,当我选择一个选项时,它会聚焦。
此 div 将具有tabindex="-1"
因此无法通过键盘访问(仅以编程方式)。
第二个我选择一个选项,我们关注这个 div,然后开始插入文本。
这样,当我按下<kbd>Tab</kbd>
下一个按钮的或快捷方式时,它会跳转到第一个新选项。
我在下面创建了一个基本的小提琴,它需要一些改进,但给你一个模式来测试/使用。(全屏显示,否则您可能看不到添加的按钮等)
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>
小提琴的解释
所以他们的关键点是在加载更多内容之前关注该 div 的 JavaScript 中视觉隐藏和伴随的行。 <div>
div 让我们在单击按钮后更改焦点。我还在该 div 内的文本中添加了“加载”以添加其他目的,因为您的应用程序将由 AJAX 驱动。
div 有tabindex="-1"
所以它不能接收焦点。我还添加aria-hidden="true"
了它,并在单击选项按钮时将其关闭,就在给它焦点之前。
当焦点在现实世界中离开这个 div 时,我会将它切换回来,但我并没有在快速演示小提琴中这样做。
这不是“失败”的WCAG吗?
是的!在此示例中,我将不可聚焦的项目设为可聚焦,但它没有任何操作。我仍然认为这比使它成为更好,<button>
因为这意味着它有一个动作。显然并不完美。
然而,WCAG 的关键部分是“ G ”——它们是指导方针。我建议的方式是“hack”或妥协,因为我对开发时间和技术限制持现实态度。
您可以在没有 div 的情况下执行上述操作的“正确”方法是进行一些仔细的焦点管理。有了无限的时间和预算,我肯定会这样做。
但是考虑到您不必只考虑Tab屏幕阅读器的键(您可以通过链接、按钮、标题、部分等进行导航),这在尝试拦截击键时会变成一场噩梦,因此上述是最简单的方法我可以考虑这样做。
替代模式。
因为前面的例子'失败'WCAG 还有另一种选择,但是我认为这更糟并且引入了很多问题。
每次将 div 中的文本替换为新文本。
不利的一面是您每次都需要提供“上一个项目”按钮并跟踪它们,有利的一面是这将符合 WCAG(尽管我认为即使您“通过”标准也不可用。)
还提供那些“上一个”按钮再次引入了焦点管理的许多问题(我们什么时候让它们可见,它们是否获得焦点,它们是否会增加更多的混乱?)。
可以把它想象成一个表单向导模式,每组问题都是一个“步骤”,因此您可以返回之前的步骤,而您只能在屏幕上看到当前步骤。
我包括了这个,因为这个模式可以通过一些想法来扩展,并给 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>