ARIA:選項卡角色
ARIA 的 tab 角色指示 tablist 中的一個互動式元素,當啟用時,會顯示其關聯的 tabpanel。
<button role="tab" aria-selected="true" aria-controls="tabpanel-id" id="tab-id">
Tab label
</button>
描述
具有 tab 角色的元素控制具有 tabpanel 角色的關聯元素的可見性。常見的使用者體驗模式是在內容區域的上面或旁邊有一組可視標籤,選擇不同的標籤會更改內容並使選定的標籤比其他標籤更突出。
具有 tab 角色的元素必須是具有 tablist 角色的元素的子元素,或者其 id 是 tablist 的 aria-owns 屬性的一部分。這種組合向輔助技術標識元素屬於一組相關元素。一些輔助技術將提供 tablist 中的 tab 角色元素的數量,並告知使用者他們當前定位了哪個 tab。此外,具有 tab 角色的元素應該包含 aria-controls 屬性,該屬性透過該元素的 id 標識相應的 tabpanel(具有 tabpanel 角色)。當具有 tabpanel 角色的元素獲得焦點,或者其子元素獲得焦點時,這表示連線的具有 tab 角色的元素是 tablist 中的活動選項卡。
當具有 tab 角色的元素被選中或處於活動狀態時,它們的 aria-selected 屬性應設定為 true。否則,它們的 aria-selected 屬性應設定為 false。當單個可選擇 tablist 被選中或處於活動狀態時,其他選項卡面板的 hidden 屬性應設定為 true,直到使用者選擇與該選項卡面板關聯的選項卡。當多個可選擇 tablist 被選中或處於活動狀態時,其對應的受控 tabpanel 應將其 aria-expanded 屬性設定為 true,並將其 hidden 屬性設定為 false,否則反之。
所有後代都是演示性的
在平臺無障礙 API 中表示時,某些型別的使用者介面元件只能包含文字。無障礙 API 沒有方法來表示 tab 中包含的語義元素。為了解決此限制,瀏覽器會自動將角色 presentation 應用於任何 tab 元素的所有後代元素,因為這是一個不支援語義子元素的角色。
例如,考慮以下 tab 元素,它包含一個標題。
<div role="tab"><h3>Title of my tab</h3></div>
由於 tab 的後代是演示性的,因此以下程式碼等效
<div role="tab"><h3 role="presentation">Title of my tab</h3></div>
從輔助技術使用者的角度來看,標題不存在,因為之前的程式碼片段在 無障礙樹 中等效於以下內容
<div role="tab">Title of my tab</div>
關聯的角色和屬性
aria-selected-
布林值
aria-controls-
具有
tabpanel角色的元素的id - id
-
內容
鍵盤互動
| 鍵 | 操作 |
|---|---|
| Tab | 當焦點在 tablist 之外時,將焦點移動到活動選項卡。如果焦點在活動選項卡上,則將焦點移動到鍵盤焦點順序中的下一個元素,理想情況下是活動選項卡的關聯 tabpanel。 |
| → | 聚焦並可選地啟用選項卡列表中的下一個選項卡。如果當前選項卡是選項卡列表中的最後一個選項卡,則會啟用第一個選項卡。 |
| ← | 聚焦並可選地啟用選項卡列表中的上一個選項卡。如果當前選項卡是選項卡列表中的第一個選項卡,則會啟用最後一個選項卡。 |
| Delete | 如果允許,則會從選項卡列表中刪除當前選定的選項卡。 |
所需的 JavaScript 功能
注意:雖然有一些方法可以在沒有 JavaScript 的情況下構建類似選項卡的功能,但沒有使用僅 HTML 和 CSS 的組合可以替代上述為具有內容的可訪問選項卡所需的功能集。
示例
此示例將tab角色與tablist和帶有tabpanel元素結合使用,以建立一個互動式選項卡內容組。在這裡,我們將內容組放在一個div中,我們的tablist具有aria-label,用於輔助技術的標籤。每個tab都是一個帶有前面提到的屬性的button。第一個tab同時具有tabindex="0"和aria-selected="true"屬性。這兩個屬性必須始終協調一致,因此當選擇另一個選項卡時,它將擁有tabindex="0"和aria-selected="true"屬性。所有未選中的選項卡必須具有aria-selected="false"和tabindex="-1"。
所有tabpanel元素都具有tabindex="0",以使它們可製表,除了當前活動元素之外,所有元素都具有hidden屬性。當tabpanel透過 JavaScript 變得可見時,hidden屬性將被刪除。應用了一些基本的樣式,這些樣式重新設計按鈕,並更改了z-index of tab 元素,以使活動元素的tab元素連線到tabpanel的錯覺,以及非活動元素位於活動tabpanel後面的錯覺。
<div class="tabs">
<div role="tablist" aria-label="Sample Tabs">
<button
role="tab"
aria-selected="true"
aria-controls="panel-1"
id="tab-1"
tabindex="0">
First Tab
</button>
<button
role="tab"
aria-selected="false"
aria-controls="panel-2"
id="tab-2"
tabindex="-1">
Second Tab
</button>
<button
role="tab"
aria-selected="false"
aria-controls="panel-3"
id="tab-3"
tabindex="-1">
Third Tab
</button>
</div>
<div id="panel-1" role="tabpanel" tabindex="0" aria-labelledby="tab-1">
<p>Content for the first panel</p>
</div>
<div id="panel-2" role="tabpanel" tabindex="0" aria-labelledby="tab-2" hidden>
<p>Content for the second panel</p>
</div>
<div id="panel-3" role="tabpanel" tabindex="0" aria-labelledby="tab-3" hidden>
<p>Content for the third panel</p>
</div>
</div>
我們需要使用 JavaScript 完成兩件事:我們需要使用左右箭頭更改tab元素的焦點和選項卡索引,並且我們需要在點選tab時更改活動tab和tabpanel。
為了完成第一個,我們監聽tablist上的keydown事件。如果事件的key是ArrowRight或ArrowLeft,我們將對此事件做出反應。我們首先將當前tab元素的tabindex設定為 -1,使其不再可製表。然後,如果按下右箭頭,我們將選項卡焦點計數器增加 1。如果計數器大於我們擁有的tab元素數量,我們將透過將該計數器設定為 0 來迴圈回到第一個選項卡。如果按下左箭頭,我們將選項卡焦點計數器減少 1,如果它小於 0,我們將它設定為選項卡元素數量減 1(以到達最後一個元素)。最後,我們將focus設定為索引等於選項卡焦點計數器的tab元素,並將它的tabindex設定為 0,使其可製表。
為了處理更改活動tab和tabpanel,我們有一個函式,它接收事件,獲取觸發事件的元素,觸發元素的父元素及其祖父母元素。然後,我們找到父元素中所有具有aria-selected="true"的選項卡,將其設定為false,然後將觸發元素的aria-selected設定為true。之後,我們在祖父母元素中找到所有tabpanel元素,使它們全部hidden,最後選擇id等於觸發tab的aria-controls的元素,並刪除hidden屬性,使其可見。
window.addEventListener("DOMContentLoaded", () => {
// Only handle one particular tablist; if you have multiple tab
// lists (might even be nested), you have to apply this code for each one
const tabList = document.querySelector('[role="tablist"]');
const tabs = tabList.querySelectorAll(':scope > [role="tab"]');
// Add a click event handler to each tab
tabs.forEach((tab) => {
tab.addEventListener("click", changeTabs);
});
// Enable arrow navigation between tabs in the tab list
let tabFocus = 0;
tabList.addEventListener("keydown", (e) => {
// Move right
if (e.key === "ArrowRight" || e.key === "ArrowLeft") {
tabs[tabFocus].setAttribute("tabindex", -1);
if (e.key === "ArrowRight") {
tabFocus++;
// If we're at the end, go to the start
if (tabFocus >= tabs.length) {
tabFocus = 0;
}
// Move left
} else if (e.key === "ArrowLeft") {
tabFocus--;
// If we're at the start, move to the end
if (tabFocus < 0) {
tabFocus = tabs.length - 1;
}
}
tabs[tabFocus].setAttribute("tabindex", 0);
tabs[tabFocus].focus();
}
});
});
function changeTabs(e) {
const targetTab = e.target;
const tabList = targetTab.parentNode;
const tabGroup = tabList.parentNode;
// Remove all current selected tabs
tabList
.querySelectorAll(':scope > [aria-selected="true"]')
.forEach((t) => t.setAttribute("aria-selected", false));
// Set this tab as selected
targetTab.setAttribute("aria-selected", true);
// Hide all tab panels
tabGroup
.querySelectorAll(':scope > [role="tabpanel"]')
.forEach((p) => p.setAttribute("hidden", true));
// Show the selected panel
tabGroup
.querySelector(`#${targetTab.getAttribute("aria-controls")}`)
.removeAttribute("hidden");
}
最佳實踐
建議使用<button>元素,其角色為tab,以實現其內建的功能和可訪問性功能,而不是需要自己新增它們。為了控制具有tab角色的元素的選項卡鍵功能,建議將所有非活動元素設定為tabindex="-1",並將活動元素設定為tabindex="0"。
優先順序順序
相關的屬性是什麼,以及此屬性或屬性的讀取順序,哪個屬性優先於此屬性,哪個屬性將被覆蓋。
規範
| 規範 |
|---|
| 可訪問的富網際網路應用 (WAI-ARIA) # tab |
| 未知規範 |