ARIA:radio 角色

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 单选按钮 本身附带的所有功能都需要使用 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">) 有两种状态(“选中”或“未选中”)。类似地,具有 role="radio" 的元素可以通过 aria-checked 属性公开两种状态:true 表示选中状态,false 表示未选中状态。对于单选按钮,aria-checkedmixed 无效。

如果选中了单选按钮,则单选按钮元素的 aria-checked 设置为 true。如果未选中,则其 aria-checked 设置为 false

每个单选按钮元素都具有角色 radio。radio 角色应始终嵌套在 radiogroup 中的其他关联 radio 中。如果无法将单选按钮嵌套在 radio 组中,请在 radiogroup 元素的 aria-owns 属性的值中使用非分组 radio 的 id 作为以空格分隔的值列表,以指示 radiogroup 与其 radio 成员的关系。

每个 radio 元素都由其内容标记,具有 aria-labelledby 引用的可见标签,或使用 aria-label 指定的标签。包含的 radiogroup 元素应具有 aria-labelledby 引用的可见标签或使用 aria-label 指定的标签。如果存在提供有关 radio 组或每个 radio 按钮的其他信息的元素,则这些元素应由 radiogroup 元素或使用 aria-describedby 属性的 radio 元素引用。

由于 radio 是一个交互式控件;它必须可聚焦且可通过键盘访问。如果角色应用于不可聚焦的元素,请使用 tabindex 属性更改此设置。激活 radio 的预期键盘快捷键是 空格键。使用 JavaScript 将 aria-checked 属性切换到 true,当 radio 被选中时,同时确保该组中的所有其他 radio 角色都设置为 aria-checked="false"

要以编程方式指示必须从 radio 组中选择单选按钮,必须在 radiogroup 元素上指定 aria-required 属性(值为 true)。不建议在单个 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 的值定义了单选按钮的状态。当与单选按钮元素一起使用时,该属性具有两个可能的值之一

true

单选按钮被选中。

false

单选按钮未被选中。

注意:如果在默认情况下不接受键盘焦点的元素(例如,<div><span>)上使用 role="radio",请使用 tabindex 属性

键盘交互

Tab + Shift

将焦点移入和移出单选按钮组。当焦点移入单选按钮组时,如果某个单选按钮已被选中,则焦点将设置在选中的按钮上。如果没有任何单选按钮被选中,则焦点将设置在组中的第一个单选按钮上。

空格键

如果单选按钮未被选中,则选中它。取消选中单选按钮组中先前选中的单选按钮。

右箭头下箭头

将焦点移动到组中的下一个单选按钮并选中它,同时取消选中先前获得焦点的单选按钮。如果焦点位于最后一个单选按钮上,则焦点将移动到第一个单选按钮。

左箭头上箭头

将焦点移动到组中的上一个单选按钮并选中它,同时取消选中先前获得焦点的单选按钮。如果焦点位于第一个单选按钮上,则焦点将移动到最后一个单选按钮。

工具栏中的单选按钮

由于箭头键用于在工具栏元素之间导航,并且 Tab 键用于将焦点移入和移出工具栏,因此当单选按钮组嵌套在工具栏中时,单选按钮组的键盘交互方式略有不同于不在工具栏内的单选按钮组。有关更多信息,请参阅 radiogroup 键盘交互

必需的 JavaScript

onClick

处理单选按钮和关联标签上的鼠标点击,这将通过更改 aria-checked 属性的值以及单选按钮的外观(使其对视力正常的用户显示为选中或未选中)来更改单选按钮的状态。

onKeyPress

处理用户按下 空格键 以更改单选按钮状态的情况,这将通过更改 aria-checked 属性的值以及单选按钮的外观(使其对视力正常的用户显示为选中或未选中)来更改单选按钮的状态。

示例

以下示例使用 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

需要大量的 JavaScript 代码才能将非语义 HTML 元素转换为单选按钮。

js
// initialize all the radio role elements

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

for (let i = 0, groups = radioGroups.length; i < groups; i++) {
  const radios = radioGroups[i].querySelectorAll("[role=radio]");
  for (let j = 0, radiobuttons = radios.length; j < radios; j++) {
    radios[j].addEventListener("keydown", function () {
      handleKeydown();
    });
    radios[j].addEventListener("click", function () {
      handleClick();
    });
  }
}

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

// handle key presses
let handleKeydown = function (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

setChecked = function () {
  // 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 重新创建单选按钮的功能。

另请参阅