radio 按钮互斥依赖 name 属性完全一致;value 是提交值而非显示文本,须合法简短;label 必须正确关联以保障可点击性;未选中时 querySelector 返回 null,且表单不提交该字段。

name 属性不一致就根本不是一组
浏览器只靠 name 值判断哪些 <input type="radio"> 属于同一组,同名才互斥。漏写 name、拼错(比如 gender 写成 gander)、大小写混用(Theme vs theme),都会让按钮各自独立,点哪个都选中。
常见错误写法:
<input type="radio" value="a"> <input type="radio" value="b">
正确写法必须显式统一 name:
- 所有同组 radio 的
name值完全相同(建议全小写、下划线分隔) - 不同逻辑组(如“颜色”和“尺寸”)必须用不同
name,否则会意外联动 - 动态渲染时注意模板变量是否污染了
name值,比如 Vue/React 中拼接出name="opt_{{id}}"导致每组唯一
value 不是显示文字,而是提交值
用户看到的“男”“女”,和后端收到的 "male" 是两回事。value 是表单提交或 JS 读取时实际使用的字符串,它不会显示在页面上,也不该是中文或空格。
容易踩的坑:
- 用
value="男":可能触发 URL 编码问题、JSON 解析失败、数据库字段截断 - 省略
value:浏览器默认提交on,后端拿到gender=on完全无法区分选项 - 多个 radio 用相同
value:虽然能互斥,但提交后无法分辨用户到底选了哪个
推荐写法:value 应简短、合法、语义清晰,比如 "light"/"dark"、"public"/"private"、"1"/"0"。
label 不只是美化,是功能刚需
没包 label 或没用 for 关联,用户点文字根本不会触发选中——这不是体验差,是功能缺失。移动端尤其明显,原生 radio 点击热区通常只有 12–16px,手指根本点不准。
安全可靠的写法只有两种:
- 包裹式(推荐):
<label><input type="radio" name="mode" value="auto"> 自动模式</label> - for+id 式(仅限结构受限时):
<input id="m1" type="radio" name="mode" value="manual"><label for="m1">手动模式</label>,注意id必须唯一且拼写严格匹配 - 禁用
display: none隐藏原生 radio:会导致屏幕阅读器跳过、iOS Safari change 事件不触发;要用position: absolute; left: -9999px配合视觉模拟
JS 读取时 null 是常态,不是异常
document.querySelector('input[name="plan"]:checked') 在没人选中时返回 null,不是 undefined。直接链式取 .value 就会报 Cannot read property 'value' of null。
正确读取姿势:
const selected = document.querySelector('input[name="plan"]:checked');
const planValue = selected ? selected.value : '';
其他注意事项:
- 别用
getElementsByName加循环遍历,冗余且易错 - 别假设一定有默认选中——
checked是可选属性,用户可能还没操作,或者你忘了加 - 如果需要“必选”语义,靠 HTML
required无效(radio 不支持 required 单独生效),得靠 JS 校验或服务端兜底
最常被忽略的一点:整组没选中时,表单提交压根不发送该 name 字段,后端收不到任何值,不是空字符串,是字段缺失。这点在接口设计和校验逻辑里必须明确处理。

















