不是一个完整的答案
这是一个太大的问题,无法完整回答,需要考虑数百件事,并且仅通过 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>
小提琴的解释
因此,它们的关键点是 visually-hidden <div> 和 JavaScript 中关注该 div 在加载更多内容之前的随附行。
div 让我们在单击按钮后更改焦点。我还在该 div 内的文本中添加了“加载”以添加其他用途,因为您的应用程序将支持 AJAX。
div 有tabindex="-1",因此它无法获得焦点。我还在其中添加了aria-hidden="true",并在单击选项按钮时将其关闭,就在赋予它焦点之前。
当焦点离开现实世界中的这个 div 时,我会将它切换回来,但我并没有在快速演示小提琴中这样做。
这不是“失败”的WCAG吗?
是的!在此示例中,我将不可聚焦的项目设为可聚焦,但它没有任何操作。我仍然认为这比将其设为<button> 更好,因为这意味着它有一个动作。显然并不完美。
然而,WCAG 的关键部分是“G”——它们是G准则。我建议的方式是“破解”或妥协,因为我对开发时间和技术限制持现实态度。
您可以在没有 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>