ARIA: radio role

radio 角色屬於一組可勾選的單選按鈕,它們位於 radiogroup 中,在同一時間只能有一個單選按鈕被選中。

描述

單選按鈕是一種可勾選的輸入控制元件,當它與其他單選按鈕關聯時,一次只能選中其中一個。單選按鈕必須分組在 radiogroup 中,以指示它們會影響同一個值。

html
<div role="radiogroup" aria-labelledby="legend25" id="radiogroup25">
  <p id="legend25">Ipsum and lorem?</p>
  <div>
    <span
      role="radio"
      aria-checked="false"
      tabindex="0"
      aria-labelledby="q25_radio1-label"
      data-value="True"></span>
    <label id="q25_radio1-label">True</label>
  </div>
  <div>
    <span
      role="radio"
      aria-checked="false"
      tabindex="0"
      aria-labelledby="q25_radio2-label"
      data-value="False"></span>
    <label id="q25_radio2-label">False</label>
  </div>
  <div>
    <span
      role="radio"
      aria-checked="true"
      tabindex="0"
      aria-labelledby="q25_radio3-label"
      data-value="huh?"></span>
    <label id="q25_radio3-label">What is the question?</label>
  </div>
</div>

role 屬性僅新增語義; HTML radio 原生提供的所有功能都需要透過 JavaScript 和 HTML tabindex 屬性來新增。

注意: ARIA 的第一條規則是,如果一個原生的 HTML 元素或屬性具有您需要的語義和行為,請使用它,而不是重新利用一個元素並新增 ARIA。應改為使用原生的 HTML <input type="radio">(以及關聯的 <label>),它本身就提供了所有必需的功能。

html
<fieldset>
  <legend>Ipsum and lorem?</legend>
  <div>
    <input type="radio" value="True" id="q25_radio1" name="q25" />
    <label for="q25_radio1">True</label>
  </div>
  <div>
    <input type="radio" value="False" id="q25_radio2" name="q25" />
    <label for="q25_radio2">False</label>
  </div>
  <div>
    <input type="radio" value="huh?" id="q25_radio3" name="q25" checked />
    <label for="q25_radio3">What is the question?</label>
  </div>
</fieldset>

原生的 HTML 單選按鈕表單控制元件(<input type="radio">)有兩種狀態(“checked” 或 “not checked”)。同樣,具有 role="radio" 的元素可以透過 aria-checked 屬性暴露兩種狀態:true 表示選中狀態,false 表示未選中狀態。aria-checkedmixed 值不能用於單選按鈕。

如果單選按鈕被選中,則 radio 元素將 aria-checked 設定為 true。如果未選中,則將其設定為 false

每個單選按鈕元素都具有 radio 角色。radio 角色應始終與其他關聯的 radio 一起巢狀在 radiogroup 中。如果無法在標記中將單選按鈕巢狀在 radio 組中,請使用非分組 radio 的 id 作為 radiogroup 元素上 aria-owns 屬性的值,該值以空格分隔,以指示 radiogroup 與其 radio 成員的關係。

每個 radio 元素都可以透過其內容進行標記,或者由 aria-labelledby 引用的可見標籤進行標記,或者透過 aria-label 指定標籤。包含的 radiogroup 元素應透過 aria-labelledby 引用的可見標籤進行標記,或透過 aria-label 指定標籤。如果存在提供關於 radio 組或每個 radio 按鈕的附加資訊的元素,則這些元素應由 radiogroup 元素或 radio 元素使用 aria-describedby 屬性進行引用。

由於 radio 是一個互動式控制元件;它必須是可聚焦的並且可以透過鍵盤訪問。如果將角色應用於非聚焦元素,請使用 tabindex 屬性進行更改。啟用 radio 的預期鍵盤快捷鍵是 Space 鍵。當 radio 被選中時,使用 JavaScript 將 aria-checked 屬性切換為 true,並確保組中的所有其他 radio 角色都設定為 aria-checked="false"

為了以程式設計方式指示必須從 radio 組中選擇一個單選按鈕,必須在 radiogroup 元素上指定值為 truearia-required 屬性。不建議在單獨的 ARIA 單選按鈕上使用 aria-required 屬性。

所有後代都是展示性的

某些型別的使用者介面元件,當在平臺輔助功能 API 中表示時,只能包含文字。輔助功能 API 無法表示 radio 中包含的語義元素。為了處理此限制,瀏覽器會自動將 presentation 角色應用於任何 radio 元素的所有後代元素,因為它是一個不支援語義子元素的角色。

例如,考慮以下包含標題的 radio 元素。

html
<div role="radio"><h6>name of my radio</h6></div>

由於 radio 的後代是呈現性的,因此以下程式碼等效:

html
<div role="radio"><h6 role="presentation">name of my radio</h6></div>

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

html
<div role="radio">name of my radio</div>

關聯的 WAI-ARIA 角色、狀態和屬性

radiogroup 角色

單選按鈕包含在或由具有 radiogroup 角色的元素擁有。如果無法在標記中將其巢狀在 radiogroup 中,則 radiogrouparia-owns 屬性包含組中非巢狀單選按鈕的 id 值。

aria-checked

aria-checked 的值定義了 radio 的狀態。與 radio 元素一起使用時,該屬性具有以下兩種可能的值之一:

true

Radio 已選中。

false

Radio 未選中。

注意: 如果 role="radio" 用於一個不原生接受鍵盤焦點的元素,請使用 tabindex 屬性。例如,<div><span>

鍵盤互動

Tab + Shift

將焦點移入和移出 radio 組。當焦點移入 radio 組,並且已選中某個 radio 按鈕時,焦點將設定在已選中的按鈕上。如果所有 radio 按鈕都未選中,焦點將設定在組中的第一個 radio 按鈕上。

空格

如果 radio 未被選中,則選中它。取消選中 radio 組中之前已選中的 radio 按鈕。

右箭頭鍵下箭頭鍵

將焦點移至組中的下一個 radio 按鈕並選中它,同時取消選中之前具有焦點的 radio 按鈕。如果焦點在最後一個 radio 按鈕上,焦點將移至第一個 radio 按鈕。

左箭頭鍵上箭頭鍵

將焦點移至組中的上一個 radio 按鈕並選中它,同時取消選中之前具有焦點的 radio 按鈕。如果焦點在第一個 radio 按鈕上,焦點將移至最後一個 radio 按鈕。

工具欄中的 radios

由於箭頭鍵用於在工具欄元素之間導航,而 Tab 鍵用於將焦點移入和移出工具欄,因此當 radio 組巢狀在工具欄中時,radio 組的鍵盤互動與非巢狀在工具欄中的 radio 組略有不同。有關更多資訊,請參閱 radiogroup 鍵盤互動

必需的 JavaScript

onClick

處理對 radio 及其關聯標籤的滑鼠點選,透過更改 aria-checked 屬性的值來更改 radio 的狀態,並改變 radio 的外觀,使其對可見使用者顯示為已選中或未選中。

onKeyPress

處理使用者按下 Space 鍵以更改 radio 的狀態的情況,透過更改 aria-checked 屬性的值來更改 radio 的狀態,並改變 radio 的外觀,使其對可見使用者顯示為已選中或未選中。

示例

以下示例使用 ARIA 來修改其他通用元素,使其暴露為單選按鈕。CSS 和 JavaScript 用於在視覺上和程式設計上修改元素的選中或未選中狀態。

HTML

html
<div role="radiogroup" aria-labelledby="legend" id="radiogroup">
  <p id="legend">
    Should you be using the <code>radio</code> role or
    <code>&lt;input type="radio"></code>?
  </p>
  <div>
    <span
      role="radio"
      aria-checked="true"
      tabindex="0"
      aria-labelledby="ariaLabel"
      data-value="True"></span>
    <label id="ariaLabel">ARIA role</label>
  </div>
  <div>
    <span
      role="radio"
      aria-checked="false"
      tabindex="0"
      aria-labelledby="htmllabel"
      data-value="False"></span>
    <label id="htmllabel">HTML <code>&lt;input type="radio"></code></label>
  </div>
</div>

CSS

css
[role="radio"] {
  padding: 5px;
}

[role="radio"][aria-checked="true"]::before {
  content: "(x)";
  font-family: monospace;
}

[role="radio"][aria-checked="false"]::before {
  content: "( )";
  font-family: monospace;
}

JavaScript

要將非語義 HTML 製作成單選按鈕,需要大量的 JavaScript。

js
// initialize all the radio role elements

const radioGroups = document.querySelectorAll('[role="radiogroup"]');

for (const radioGroup of radioGroups) {
  const radios = radioGroup.querySelectorAll("[role=radio]");
  for (const radio of radios) {
    radio.addEventListener("keydown", handleKeydown);
    radio.addEventListener("click", handleClick);
  }
}

// handle mouse and touch events
function handleClick(event) {
  setChecked(this);
  event.stopPropagation();
  event.preventDefault();
}

// handle key presses
function handleKeydown(event) {
  switch (event.code) {
    case "Space":
    case "Enter":
      currentChecked();
      break;

    case "ArrowUp":
    case "ArrowLeft":
      previousRadioChecked();
      break;

    case "ArrowDown":
    case "ArrowRight":
      nextItemChecked();
      break;

    default:
      break;
  }
  event.stopPropagation();
  event.preventDefault();
}

// when a radio is selected, give it focus, set checked to true;
// ensure all other radios in radio group are not checked

function setChecked() {
  // uncheck all the radios in group
  // iterated through all the radios in radio group
  // eachRadio.tabIndex = -1;
  // eachRadio.setAttribute('aria-checked', 'false');
  // set the selected radio to checked
  // thisRadio.setAttribute('aria-checked', 'true');
  // thisRadio.tabIndex = 0;
  // thisRadio.focus();
  // set the value of the radioGroup to the value of the currently selected radio
}

如果使用了語義 HTML 元素,並且一組單選按鈕中每個單選按鈕的名稱相同,則不需要 JavaScript(甚至不需要 CSS)。

html
<fieldset>
  <legend>
    Should you be using the <code>radio</code> role or
    <code>&lt;input type="radio"></code>?
  </legend>
  <div>
    <input type="radio" name="bestPractices" id="ariaLabel" value="True" />
    <label for="ariaLabel">ARIA role</label>
  </div>
  <div>
    <input type="radio" name="bestPractices" id="htmllabel" value="False" />
    <label for="htmllabel">HTML <code>&lt;input type="radio"></code></label>
  </div>
</fieldset>

最佳實踐

ARIA 的第一條規則是:如果一個原生的 HTML 元素或屬性具有您需要的語義和行為,請使用它,而不是重新利用一個元素並新增 ARIA 角色、狀態或屬性來使其可訪問。因此,建議使用原生的 HTML 單選按鈕表單控制元件,而不是透過 JavaScript 和 ARIA 來重新建立 radio 的功能。

另見