ARIA: tab 角色
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 被選中或處於活動狀態時,其他 tabpanel 的 hidden 屬性應設定為 true,直到使用者選擇與該 tabpanel 關聯的標籤。當多選的 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-
boolean
aria-controls-
具有
tabpanel角色的元素的id - id
-
content
鍵盤互動
| 鍵 | 動作 |
|---|---|
| 製表符 | 當焦點移出 tablist 時,將焦點移至活動標籤。如果焦點在活動標籤上,則將焦點移至鍵盤焦點順序中的下一個元素,最好是活動標籤關聯的 tabpanel。 |
| → | 聚焦並可選地啟用標籤列表中的下一個標籤。如果當前標籤是標籤列表中的最後一個標籤,則啟用第一個標籤。 |
| ← | 聚焦並可選地啟用標籤列表中的上一個標籤。如果當前標籤是標籤列表中的第一個標籤,則啟用最後一個標籤。 |
| Enter/Space | 當標籤獲得焦點時,啟用該標籤,使其關聯的面板顯示出來。 |
| Home | 聚焦並可選地啟用標籤列表中的第一個標籤。 |
| End | 聚焦並可選地啟用標籤列表中的最後一個標籤。 |
| 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 屬性。
注意:如果 tab panel 中的第一個元素是可聚焦的(例如連結),則在 tab panel 上設定 tabindex 是不必要的,因為聚焦到該連結也會開始讀取 panel 的內容。但是,如果集合中有任何 panel 的第一個內容元素不可聚焦,則 tab 集合中的所有 tabpanel 元素都應該是可聚焦的,以便螢幕閱讀器使用者能夠一致地導航到 panel 內容。
<div class="tabs">
<div role="tablist" aria-label="Select your operating system">
<button
role="tab"
aria-selected="true"
aria-controls="panel-1"
id="tab-1"
tabindex="0">
Windows
</button>
<button
role="tab"
aria-selected="false"
aria-controls="panel-2"
id="tab-2"
tabindex="-1">
macOS
</button>
<button
role="tab"
aria-selected="false"
aria-controls="panel-3"
id="tab-3"
tabindex="-1">
Linux
</button>
</div>
<div class="tab-panels">
<div id="panel-1" role="tabpanel" tabindex="0" aria-labelledby="tab-1">
<p>How to run this application on Windows</p>
</div>
<div
id="panel-2"
role="tabpanel"
tabindex="0"
aria-labelledby="tab-2"
hidden="hidden">
<p>How to run this application on macOS</p>
</div>
<div
id="panel-3"
role="tabpanel"
tabindex="0"
aria-labelledby="tab-3"
hidden="hidden">
<p>How to run this application on Linux</p>
</div>
</div>
</div>
應用了一些基本樣式,這些樣式重新設定了按鈕的樣式,並更改了 tab 元素的 z-index,以造成它與活動元素的 tabpanel 連線的錯覺,並造成非活動元素位於活動 tabpanel 後面的錯覺。您需要清楚地區分活動標籤和非活動標籤,例如使用更粗的邊框或更大的尺寸。
使用者互動由 JavaScript 處理。我們首先獲取對 tablist、其中的所有 tab 元素、tabpanel 元素容器以及該容器中的所有 tabpanel 元素的引用。這基於我們對 HTML 結構的一些假設,因此如果您更改了結構,則需要更改此程式碼。如果您在一個頁面上有多個標籤式介面,您可以將此程式碼包裝在一個函式中,並將 tabsContainer 作為引數傳遞。
const tabsContainer = document.querySelector(".tabs");
const tabList = tabsContainer.querySelector(':scope > [role="tablist"]');
const tabs = Array.from(tabList.querySelectorAll(':scope > [role="tab"]'));
const tabPanelsContainer = tabsContainer.querySelector(":scope > .tab-panels");
const tabPanels = Array.from(
tabPanelsContainer.querySelectorAll(':scope > [role="tabpanel"]'),
);
對於鍵盤互動,我們在 tablist 上監聽 keydown 事件。在此演示中,我們選擇不啟用 tab,當用戶使用箭頭鍵導航時,而是僅移動焦點。如果您想在 tab 獲得焦點時顯示它,您可以呼叫 showTab() 函式(稍後定義),而不是僅呼叫新標籤的 focus()。
tabList.addEventListener("keydown", (e) => {
const currentTab = e.target;
const currentIndex = tabs.indexOf(currentTab);
if (currentIndex === -1) return; // Exit if the focused element is not a tab
let newIndex = 0;
switch (e.key) {
case "ArrowRight":
newIndex = (currentIndex + 1) % tabs.length;
break;
case "ArrowLeft":
newIndex = (currentIndex - 1 + tabs.length) % tabs.length;
break;
case "Home":
newIndex = 0;
break;
case "End":
newIndex = tabs.length - 1;
break;
default:
return; // Exit if the key is not recognized
}
e.preventDefault();
e.stopPropagation();
tabs[newIndex].focus();
});
只有當用戶在 tab 獲得焦點時按下 Enter 或 Space,或者透過單擊 tab 時,tab panel 才會啟用。我們首先定義一個 showTab() 函式,該函式接受要顯示的 tab 元素。
function showTab(targetTab) {
// Unselect other tabs and set this tab as selected
for (const tab of tabs) {
if (tab === targetTab) continue;
tab.setAttribute("aria-selected", false);
tab.tabIndex = -1;
}
targetTab.setAttribute("aria-selected", true);
targetTab.tabIndex = 0;
// Hide other tab panels and show the selected panel
const targetTabPanel = document.getElementById(
targetTab.getAttribute("aria-controls"),
);
for (const panel of tabPanels) {
if (panel === targetTabPanel) continue;
panel.hidden = true;
}
targetTabPanel.hidden = false;
}
現在,我們可以在 click 事件或 keydown 事件上呼叫此函式。
tabs.forEach((tab) => {
tab.addEventListener("click", (e) => {
showTab(e.target);
});
tab.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
e.stopPropagation();
showTab(e.target);
}
});
});
最佳實踐
建議使用具有 tab 角色的 <button> 元素,因為它們具有內建的功能和可訪問性,而不是需要自己新增。對於 tab 角色的元素的 tab 鍵功能控制,建議將所有非活動元素設定為 tabindex="-1",並將活動元素設定為 tabindex="0"。
優先順序順序
相關的屬性有哪些,該屬性或屬性的讀取順序是什麼,哪個屬性將優先於此屬性,哪個屬性將被覆蓋。
規範
| 規範 |
|---|
| 無障礙富網際網路應用程式 (WAI-ARIA) # tab |
| 未知規範 |