ARIA: tab 角色

ARIA 的 tab 角色表示 tablist 內的一個互動式元素,當該元素被啟用時,它會顯示其關聯的 tabpanel

html
<button role="tab" aria-selected="true" aria-controls="tabpanel-id" id="tab-id">
  Tab label
</button>

描述

具有 tab 角色的元素會控制具有 tabpanel 角色的關聯元素的可視性。常見的使用者體驗模式是,視覺標籤組位於內容區域的上方或側面,選擇不同的標籤會更改內容,並使選定的標籤比其他標籤更突出。

具有 tab 角色的元素必須tablist 角色元素的子元素,或者其 idtablistaria-owns 屬性的一部分。此組合向輔助技術表明該元素是一個相關元素組的一部分。一些輔助技術會提供 tablisttab 角色元素的計數,並告知使用者他們當前已定位到哪個 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 元素。

html
<div role="tab"><h3>Title of my tab</h3></div>

由於 tab 的後代是表現型的,因此以下程式碼等效

html
<div role="tab"><h3 role="presentation">Title of my tab</h3></div>

從輔助技術使用者的角度來看,標題不存在,因為前面的程式碼片段在輔助功能樹中等同於以下內容:

html
<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 內容。

html
<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 作為引數傳遞。

js
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()

js
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 獲得焦點時按下 EnterSpace,或者透過單擊 tab 時,tab panel 才會啟用。我們首先定義一個 showTab() 函式,該函式接受要顯示的 tab 元素。

js
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 事件上呼叫此函式。

js
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
未知規範

另見