为什么我的屏幕 reader 在激活时不宣布选定的选项卡
Why does my screen reader not announce the selected tab when activated
我正在努力使我现有的选项卡组件可访问,我的设计基于 W3C 的 Example of Tabs with Manual Activation。
HTML
<div class="tab-container" lang="en">
<div class="tabs" role="tablist">
<button class="tab" aria-selected="true" href="#" role="tab" data-tab-name="tab1" tabindex="0">
<!-- tab name -->
</button>
<!-- more tabs -->
</div>
<div class="tab-content" data-name="tab1" role="tabpanel" tabindex="0">
<!-- tab panel content -->
</div>
<!-- more tab panels -->
</div>
JQuery
function getTabContent($tabContents, tabName) {
return $tabContents.filter('[data-name="' + tabName + '"]');
}
function setSelectedTab($tab) {
var tabName = $tab.data('tab-name'),
$tabSet = $tab.closest('.tabs'),
$tabContents = $tab.closest('.tab-container').find('.tab-content');
// update the tab indices and aria attributes
$tabSet.find('.tab').attr('aria-selected', 'false').attr('tabindex', '-1');
$tab.attr('aria-selected', 'true').removeAttr('tabindex');
$tabContents.addClass('hidden');
getTabContent($tabContents, tabName).removeClass('hidden');
}
function handleTabSelection(event) {
var $tab = $(event.target);
if ($tab.data('tab-name')) {
event.preventDefault();
setSelectedTab($tab);
$tab.focus();
}
}
// Our tab control needs to be used in many places on our site, we cannot guarantee that all devs will use unique IDs
// so we need to generate them here
function initTabs($tabContainer) {
var $tabList = $tabContainer.find('.tabs'),
$tabContents = $tabContainer.find('.tab-content'),
tabSetName = $tabList.data.name,
tabIdPrefix = 'tab-',
contentIdPrefix = 'tab-content-';
// add unique ids and labels
$tabList.children().each(function() {
var $tab = $(this),
tabName = $tab.data('tab-name'),
$tabContent = getTabContent($tabContents, tabName),
tabId = getUniqueId(tabIdPrefix + tabName),
contentId = getUniqueId(contentIdPrefix + tabName);
// add the unique id and associate the link with the content
$tab.attr('id', tabId).attr('aria-controls', contentId);
// add the unique id and use the link as the label for the content
$tabContent.attr('id', contentId).attr('aria-labelledby', tabId);
});
}
function getUniqueId(id, index) {
var newId = id;
if (index) {
newId += '--' + index;
index++;
} else {
index = 1;
}
if (document.getElementById(newId)) {
return getUniqueId(id, index);
}
return newId;
}
function handleKeyPress(event) {
var $tab = $(event.target);
if ($tab.is('.tab')) {
var keyCode = event.which,
$tab = $(event.target);
if (keyCode === 13 || keyCode === 32) {
// user pressed enter, or space
setSelectedTab($tab);
event.preventDefault();
} else if (keyCode === 37 || keyCode === 39) {
// the user pressed left or right
var $newTab = $tab[keyCode === 39 ? 'next' : 'prev']();
// move the focus
if ($newTab.length > 0) {
$newTab.focus();
}
event.preventDefault();
}
}
}
$('.tabs').click(handleTabSelection);
$('.tabs').keyup(handleKeyPress);
$('.tab-container').each(function() {
initTabs($(this));
});
用户可以使用左右键在选项卡列表中移动焦点,并输入或space到select一个选项卡。
然而,当用户 select 选择一个选项卡时,屏幕 reader 只是宣布 "selected" 在 W3C 的示例中,它宣布选项卡名称后跟 "selected"。
我正在 Firefox 中使用 NVDA 进行测试,以下是我的重现步骤:
- 将焦点设置在 "Nils Frahm" 选项卡上
- 按TAB
- 你应该听到 "Agnes Obel tab two of three"
- 按回车
- 你应该听到 "Agnes Obel tab selected tab two of three"
这正是 W3C 示例中发生的情况,但在我的示例中,最后一步仅读取 "selected"。
我已经尝试尽可能地匹配他们的示例,但我还没有弄清楚如何让我的示例在激活时宣布选项卡名称。
什么会导致 NVDA 在激活后跳过读取选项卡名称?
我发现了解决问题的方法,但到目前为止,还不知道问题存在的原因。
当我在我选择的选项卡上添加 after
CSS 规则时,屏幕 reader 在选择时开始阅读内容。
.tab[aria-selected="true"]::after {
content: '';
}
如果我将 after
标记添加到所有选项卡,问题仍然存在;它只需要在选定的元素上。
我猜这是在欺骗屏幕 reader 以为内容已更改,因此它会读取新的选项卡名称。
我正在努力使我现有的选项卡组件可访问,我的设计基于 W3C 的 Example of Tabs with Manual Activation。
HTML
<div class="tab-container" lang="en">
<div class="tabs" role="tablist">
<button class="tab" aria-selected="true" href="#" role="tab" data-tab-name="tab1" tabindex="0">
<!-- tab name -->
</button>
<!-- more tabs -->
</div>
<div class="tab-content" data-name="tab1" role="tabpanel" tabindex="0">
<!-- tab panel content -->
</div>
<!-- more tab panels -->
</div>
JQuery
function getTabContent($tabContents, tabName) {
return $tabContents.filter('[data-name="' + tabName + '"]');
}
function setSelectedTab($tab) {
var tabName = $tab.data('tab-name'),
$tabSet = $tab.closest('.tabs'),
$tabContents = $tab.closest('.tab-container').find('.tab-content');
// update the tab indices and aria attributes
$tabSet.find('.tab').attr('aria-selected', 'false').attr('tabindex', '-1');
$tab.attr('aria-selected', 'true').removeAttr('tabindex');
$tabContents.addClass('hidden');
getTabContent($tabContents, tabName).removeClass('hidden');
}
function handleTabSelection(event) {
var $tab = $(event.target);
if ($tab.data('tab-name')) {
event.preventDefault();
setSelectedTab($tab);
$tab.focus();
}
}
// Our tab control needs to be used in many places on our site, we cannot guarantee that all devs will use unique IDs
// so we need to generate them here
function initTabs($tabContainer) {
var $tabList = $tabContainer.find('.tabs'),
$tabContents = $tabContainer.find('.tab-content'),
tabSetName = $tabList.data.name,
tabIdPrefix = 'tab-',
contentIdPrefix = 'tab-content-';
// add unique ids and labels
$tabList.children().each(function() {
var $tab = $(this),
tabName = $tab.data('tab-name'),
$tabContent = getTabContent($tabContents, tabName),
tabId = getUniqueId(tabIdPrefix + tabName),
contentId = getUniqueId(contentIdPrefix + tabName);
// add the unique id and associate the link with the content
$tab.attr('id', tabId).attr('aria-controls', contentId);
// add the unique id and use the link as the label for the content
$tabContent.attr('id', contentId).attr('aria-labelledby', tabId);
});
}
function getUniqueId(id, index) {
var newId = id;
if (index) {
newId += '--' + index;
index++;
} else {
index = 1;
}
if (document.getElementById(newId)) {
return getUniqueId(id, index);
}
return newId;
}
function handleKeyPress(event) {
var $tab = $(event.target);
if ($tab.is('.tab')) {
var keyCode = event.which,
$tab = $(event.target);
if (keyCode === 13 || keyCode === 32) {
// user pressed enter, or space
setSelectedTab($tab);
event.preventDefault();
} else if (keyCode === 37 || keyCode === 39) {
// the user pressed left or right
var $newTab = $tab[keyCode === 39 ? 'next' : 'prev']();
// move the focus
if ($newTab.length > 0) {
$newTab.focus();
}
event.preventDefault();
}
}
}
$('.tabs').click(handleTabSelection);
$('.tabs').keyup(handleKeyPress);
$('.tab-container').each(function() {
initTabs($(this));
});
用户可以使用左右键在选项卡列表中移动焦点,并输入或space到select一个选项卡。
然而,当用户 select 选择一个选项卡时,屏幕 reader 只是宣布 "selected" 在 W3C 的示例中,它宣布选项卡名称后跟 "selected"。
我正在 Firefox 中使用 NVDA 进行测试,以下是我的重现步骤:
- 将焦点设置在 "Nils Frahm" 选项卡上
- 按TAB
- 你应该听到 "Agnes Obel tab two of three"
- 按回车
- 你应该听到 "Agnes Obel tab selected tab two of three"
这正是 W3C 示例中发生的情况,但在我的示例中,最后一步仅读取 "selected"。
我已经尝试尽可能地匹配他们的示例,但我还没有弄清楚如何让我的示例在激活时宣布选项卡名称。
什么会导致 NVDA 在激活后跳过读取选项卡名称?
我发现了解决问题的方法,但到目前为止,还不知道问题存在的原因。
当我在我选择的选项卡上添加 after
CSS 规则时,屏幕 reader 在选择时开始阅读内容。
.tab[aria-selected="true"]::after {
content: '';
}
如果我将 after
标记添加到所有选项卡,问题仍然存在;它只需要在选定的元素上。
我猜这是在欺骗屏幕 reader 以为内容已更改,因此它会读取新的选项卡名称。