Skip to content

radio 单选按钮

type="radio" 创建单选按钮,允许用户在一组互斥的选项中选择一个。同一 name 属性的单选按钮组成一个单选组(radio group),用户只能选择组内的一个选项。本节将详细讲解单选按钮的互斥机制、value 属性、checked 默认选中,以及实现 radio group 的最佳实践。

前置知识

阅读本节前,建议先了解:search 搜索输入

基础概念

什么是单选按钮

单选按钮(radio button)是一种互斥选择控件,呈现为小圆形按钮。用户点击时按钮被选中(填充圆点),同组中的其他按钮自动取消选中。

html
<!-- 一组性别单选按钮 -->
<label><input type="radio" name="gender" value="male"> 男</label>
<label><input type="radio" name="gender" value="female"> 女</label>
<label><input type="radio" name="gender" value="other"> 其他</label>
<!-- 三个按钮的 name 相同,组成一个互斥组 -->

radio 的核心特征

特征说明
选择方式点击(或键盘空格/方向键)
互斥性同 name 的 radio 互斥
提交值选中项的 value 属性值
默认选中checked 属性
取消选择不可(一旦选中只能切换到其他选项)

语法

基本语法

html
<input
  type="radio"
  name="groupName"
  id="option1"
  value="提交值"
  checked
  disabled
>
<label for="option1">显示文本</label>

属性说明

属性说明
type"radio"指定为单选按钮
name字符串组名,同名 radio 互斥
value字符串选中时提交的值
checked布尔属性默认选中
disabled布尔属性禁用该选项
id字符串唯一标识,用于 label 关联
required布尔属性radio group 中至少选择一项

详细说明

name 属性与互斥机制

name 属性是单选按钮的核心属性。浏览器通过 name 判断哪些单选按钮属于同一组,从而实现互斥选择。

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>name 互斥机制</title>
</head>
<body>
  <h2>选择你喜欢的编程语言</h2>
  <form action="/vote" method="post">
    <!--
      name="language" 的所有 radio 组成一个互斥组
      选择 JavaScript 后,HTML 和 CSS 自动取消选中
    -->
    <label>
      <input type="radio" name="language" value="html"> HTML
    </label>
    <label>
      <input type="radio" name="language" value="css"> CSS
    </label>
    <label>
      <input type="radio" name="language" value="javascript"> JavaScript
    </label>
    <label>
      <input type="radio" name="language" value="python"> Python
    </label>

    <!--
      不同 name 的 radio 互不影响
      这里 name="level" 是另一个独立的组
    -->
    <h3>你的水平</h3>
    <label>
      <input type="radio" name="level" value="beginner"> 初学者
    </label>
    <label>
      <input type="radio" name="level" value="intermediate"> 中级
    </label>
    <label>
      <input type="radio" name="level" value="advanced"> 高级
    </label>

    <button type="submit">提交</button>
  </form>

  <!--
    提交的数据(假设选择 JavaScript 和 中级):
    language=javascript&level=intermediate
  -->
</body>
</html>

name 必须相同

同一组内的所有单选按钮必须有相同的 name 属性值。如果 name 不同,它们不属于同一组,可以实现多选(但这违背了 radio 的设计目的,应该使用 checkbox)。

value 属性

value 属性决定了选中该选项后提交到服务器的值

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>value 属性</title>
</head>
<body>
  <h2>value 的重要性</h2>
  <form action="/order" method="post">

    <!--
      错误!缺少 value 属性
      提交的值为 "on",无法区分选中的是哪个选项
    -->
    <h3>错误示例(缺少 value)</h3>
    <label><input type="radio" name="size_bad"> 小号</label>
    <label><input type="radio" name="size_bad"> 中号</label>
    <label><input type="radio" name="size_bad"> 大号</label>
    <!-- 无论选择哪个,提交的都是 size_bad=on -->

    <!--
      正确:每个 radio 都有有意义的 value
    -->
    <h3>正确示例(有 value)</h3>
    <label><input type="radio" name="size" value="S"> 小号</label>
    <label><input type="radio" name="size" value="M"> 中号</label>
    <label><input type="radio" name="size" value="L"> 大号</label>
    <!-- 选择中号提交:size=M -->

    <button type="submit">提交</button>
  </form>
</body>
</html>

严重错误

每个 <input type="radio"> 都必须设置 value 属性。如果不设置,提交的值固定为 "on",服务器无法判断用户选择了哪个选项。

checked 默认选中

checked 属性设置单选按钮的默认选中状态。同一组内只应有一个 radio 设置 checked(如果有多个,浏览器会选择最后一个)。

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>checked 默认选中</title>
</head>
<body>
  <h2>配送方式</h2>
  <form action="/order" method="post">
    <!-- 普通快递(默认选中) -->
    <label>
      <input type="radio" name="shipping" value="standard" checked>
      普通快递(免费,3~5天)
    </label>
    <br>

    <!-- 加急快递 -->
    <label>
      <input type="radio" name="shipping" value="express">
      加急快递(¥15,1~2天)
    </label>
    <br>

    <!-- 当日达 -->
    <label>
      <input type="radio" name="shipping" value="sameday">
      当日达(¥30,今天送达)
    </label>

    <button type="submit">提交</button>
  </form>
</body>
</html>

label 关联(两种方式)

为单选按钮关联 <label> 有两种方式,推荐第一种(更易维护):

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>label 关联方式</title>
  <style>
    label {
      display: block;
      margin: 8px 0;
      padding: 8px 12px;
      cursor: pointer;
      border-radius: 6px;
      transition: background-color 0.2s;
    }

    label:hover {
      background-color: #f0f0f0;
    }
  </style>
</head>
<body>
  <h2>label 关联单选按钮</h2>

  <!--
    方式一:label 包裹 input(推荐)
    点击 label 内的任意位置都能选中 radio
  -->
  <h3>方式一:嵌套(推荐)</h3>
  <label>
    <input type="radio" name="method1" value="email">
    邮箱通知
  </label>
  <label>
    <input type="radio" name="method1" value="sms">
    短信通知
  </label>
  <label>
    <input type="radio" name="method1" value="push">
    推送通知
  </label>

  <!--
    方式二:label 的 for 属性关联 input 的 id
    点击 label 文字时也能选中 radio
  -->
  <h3>方式二:for + id</h3>
  <input type="radio" name="method2" value="email" id="m2email">
  <label for="m2email">邮箱通知</label>
  <br>
  <input type="radio" name="method2" value="sms" id="m2sms">
  <label for="m2sms">短信通知</label>
  <br>
  <input type="radio" name="method2" value="push" id="m2push">
  <label for="m2push">推送通知</label>
</body>
</html>

radio group 与 required

当一组 radio 中没有默认选中项时,用户可能不选择任何选项就提交表单。使用 required 属性可以强制用户必须选择一项。

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>radio required</title>
</head>
<body>
  <h2>用户满意度调查</h2>
  <form action="/survey" method="post">

    <!--
      required 只需要设置在 radio group 的其中一个上
      浏览器会检查整个组是否至少有一项被选中
    -->
    <p>请评价我们的服务:</p>
    <label>
      <input type="radio" name="rating" value="5" required> 非常满意
    </label>
    <label>
      <input type="radio" name="rating" value="4"> 满意
    </label>
    <label>
      <input type="radio" name="rating" value="3"> 一般
    </label>
    <label>
      <input type="radio" name="rating" value="2"> 不满意
    </label>
    <label>
      <input type="radio" name="rating" value="1"> 非常不满意
    </label>

    <!--
      注意:required 不需要设置在每个 radio 上
      只要在组的任意一个 radio 上设置即可
      也可以设置在每个上(效果相同,但冗余)
    -->

    <button type="submit">提交</button>
  </form>
</body>
</html>

键盘导航

单选按钮支持键盘操作,对于无障碍访问非常重要:

按键行为
Tab聚焦到 radio group 的选中项(或第一项)
空格选中当前聚焦的 radio
上/左方向键选中上一个选项
下/右方向键选中下一个选项
Tab离开 radio group
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>键盘导航</title>
</head>
<body>
  <h2>使用键盘操作单选按钮</h2>
  <p>操作方式:Tab 聚焦 -> 方向键切换 -> 空格选中</p>

  <!--
    使用 fieldset + legend 为 radio group 添加分组标题
    这对屏幕阅读器非常重要
  -->
  <fieldset>
    <legend>选择你的操作系统</legend>

    <label><input type="radio" name="os" value="windows"> Windows</label><br>
    <label><input type="radio" name="os" value="macos"> macOS</label><br>
    <label><input type="radio" name="os" value="linux"> Linux</label><br>
    <label><input type="radio" name="os" value="other"> 其他</label>
  </fieldset>
</body>
</html>

JavaScript 操作 radio

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>JS 操作 radio</title>
</head>
<body>
  <form id="planForm">
    <fieldset>
      <legend>选择套餐</legend>
      <label><input type="radio" name="plan" value="free"> 免费版</label><br>
      <label><input type="radio" name="plan" value="pro" checked> 专业版</label><br>
      <label><input type="radio" name="plan" value="enterprise"> 企业版</label>
    </fieldset>
  </form>

  <div id="info" style="margin-top: 16px;"></div>

  <script>
    // ---- 获取当前选中的值 ----

    // 方式一:querySelector + :checked
    const selected = document.querySelector('input[name="plan"]:checked');
    console.log(selected ? selected.value : '未选择');

    // 方式二:遍历所有同 name 的 radio
    const radios = document.querySelectorAll('input[name="plan"]');
    let checkedValue = '';
    radios.forEach(radio => {
      if (radio.checked) {
        checkedValue = radio.value;
      }
    });
    console.log(checkedValue); // 输出:pro

    // ---- 监听变化 ----
    const infoDiv = document.getElementById('info');

    radios.forEach(radio => {
      radio.addEventListener('change', function() {
        if (this.checked) {
          infoDiv.textContent = '你选择了:' + this.value;
        }
      });
    });

    // ---- 通过 name 直接访问(旧方式) ----
    const plan = document.planForm.plan;
    // plan 是一个 RadioNodeList
    console.log(plan.value); // 输出当前选中的 value

    // ---- 通过 form.elements 访问 ----
    const form = document.getElementById('planForm');
    console.log(form.elements['plan'].value); // pro

    // ---- 编程方式选中 ----
    // document.querySelector('input[name="plan"][value="enterprise"]').checked = true;
  </script>
</body>
</html>

实战示例

自定义样式的单选按钮组

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>自定义 radio 样式</title>
  <style>
    body {
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
      max-width: 500px;
      margin: 40px auto;
      padding: 20px;
    }

    h2 { margin-bottom: 20px; }

    /* 隐藏原生 radio */
    .radio-group input[type="radio"] {
      position: absolute;
      opacity: 0;
      width: 0;
      height: 0;
    }

    .radio-group {
      display: flex;
      flex-direction: column;
      gap: 8px;
    }

    .radio-label {
      display: flex;
      align-items: center;
      padding: 14px 16px;
      border: 2px solid #e0e0e0;
      border-radius: 10px;
      cursor: pointer;
      transition: all 0.2s;
    }

    .radio-label:hover {
      border-color: #b0d0f0;
      background-color: #f8fbff;
    }

    /* 自定义圆形指示器 */
    .radio-indicator {
      width: 20px;
      height: 20px;
      border: 2px solid #ccc;
      border-radius: 50%;
      margin-right: 12px;
      position: relative;
      flex-shrink: 0;
      transition: border-color 0.2s;
    }

    .radio-indicator::after {
      content: '';
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      width: 10px;
      height: 10px;
      border-radius: 50%;
      background-color: #4a90d9;
      opacity: 0;
      transition: opacity 0.2s;
    }

    /* 选中状态 */
    .radio-group input[type="radio"]:checked + .radio-label {
      border-color: #4a90d9;
      background-color: #f0f7ff;
    }

    .radio-group input[type="radio"]:checked + .radio-label .radio-indicator {
      border-color: #4a90d9;
    }

    .radio-group input[type="radio"]:checked + .radio-label .radio-indicator::after {
      opacity: 1;
    }

    .radio-text {
      flex: 1;
    }

    .radio-text strong {
      display: block;
      font-size: 15px;
      color: #333;
    }

    .radio-text small {
      color: #999;
      font-size: 13px;
    }

    .radio-price {
      font-size: 18px;
      font-weight: bold;
      color: #333;
    }

    .radio-price .period {
      font-size: 13px;
      font-weight: normal;
      color: #999;
    }

    /* 聚焦样式 */
    .radio-group input[type="radio"]:focus-visible + .radio-label {
      outline: 2px solid #4a90d9;
      outline-offset: 2px;
    }

    /* 禁用状态 */
    .radio-group input[type="radio"]:disabled + .radio-label {
      opacity: 0.5;
      cursor: not-allowed;
    }
  </style>
</head>
<body>
  <h2>选择订阅计划</h2>
  <form action="/subscribe" method="post">
    <div class="radio-group">
      <input type="radio" name="plan" id="free" value="free">
      <label class="radio-label" for="free">
        <span class="radio-indicator"></span>
        <span class="radio-text">
          <strong>免费版</strong>
          <small>5GB 存储空间,基础功能</small>
        </span>
        <span class="radio-price">¥0<span class="period">/月</span></span>
      </label>

      <input type="radio" name="plan" id="pro" value="pro" checked>
      <label class="radio-label" for="pro">
        <span class="radio-indicator"></span>
        <span class="radio-text">
          <strong>专业版</strong>
          <small>100GB 存储空间,高级功能</small>
        </span>
        <span class="radio-price">¥29<span class="period">/月</span></span>
      </label>

      <input type="radio" name="plan" id="enterprise" value="enterprise">
      <label class="radio-label" for="enterprise">
        <span class="radio-indicator"></span>
        <span class="radio-text">
          <strong>企业版</strong>
          <small>无限存储空间,全部功能 + 技术支持</small>
        </span>
        <span class="radio-price">¥99<span class="period">/月</span></span>
      </label>
    </div>

    <button type="submit" style="margin-top: 20px; padding: 12px 32px; background: #4a90d9; color: white; border: none; border-radius: 8px; font-size: 16px; cursor: pointer;">
      立即订阅
    </button>
  </form>
</body>
</html>

注意事项

  1. 必须有 value 属性:缺少 value 时提交的值为 "on",无法区分选项。

  2. 同组 name 必须一致:拼写错误或大小写不同会导致互斥失败。

  3. radio 不可取消选择:一旦选中,用户只能切换到同组的其他选项,无法回到"未选择"状态。如果需要"无选择"状态,添加一个"无"选项。

  4. checked 只应设置一个:同组内多个 checked 时,浏览器只认最后一个。但在 HTML 源码中不应出现多个 checked。

  5. required 验证整个组required 只需设置在组内任意一个 radio 上。

最佳实践

  1. 始终使用 <label> 关联 radio:扩大可点击区域,提升可用性和无障碍性。

  2. 使用 <fieldset> + <legend> 分组:为 radio group 提供组标题,帮助屏幕阅读器用户理解选项组。

  3. 提供默认选中项:对大多数场景,设置一个合理的默认值,减少用户操作。

  4. 用 CSS 隐藏原生样式并自定义:原生 radio 的样式有限且跨浏览器不一致,通常需要自定义。

  5. 选项按逻辑顺序排列:如从小到大、从低到高,符合用户的认知习惯。

下一节

继续学习:checkbox 复选框

参考链接