单选按钮组必须使用相同name属性才能互斥;name错误会导致多选失效,正确做法是共用语义化name、唯一value、关联label;默认选中用checked属性;获取结果用queryselector('input[name="xxx"]:checked');无障碍需保留原生input并合理隐藏。

单选按钮组必须用相同 name 属性
浏览器靠 name 识别一组单选按钮是否互斥。如果漏写或写错 name,用户就能同时选多个——这根本不是单选组,只是几个孤立的 <input type="radio">。
常见错误:复制粘贴时只改了 id 和 value,忘了统一 name;或者用动态生成(比如 Vue/React 循环)时,name 被拼错成带索引的字符串(如 name="q1-0"),导致每项 name 都不同。
正确做法:
- 所有选项共用一个语义清晰的 name,比如 name="q3_preferred_language"
- 每个 <input> 配唯一 value(后端靠这个收答案)
- 用 id 关联 <label></label>,提升点击区域和可访问性
<div> <input type="radio" id="lang-js" name="q3_preferred_language" value="javascript"><label for="lang-js">JavaScript</label> </div> <div> <input type="radio" id="lang-py" name="q3_preferred_language" value="python"><label for="lang-py">Python</label> </div>
默认选中用 checked 属性,别用 JavaScript 模拟
想让某一项默认被选中,直接加 checked 属性就行。用 JS 在页面加载后去 .click() 或设 .checked = true 不仅多余,还可能触发不必要的事件监听、破坏表单初始状态逻辑,甚至在 SSR 场景下造成水合不一致。
注意点:
- checked 是布尔属性,写上即生效,无需 checked="true"
- 同一组里只能有一个 checked,多写也只以第一个为准
- 如果问卷是「编辑已填内容」场景,服务端应根据当前数据,在对应 <input> 上输出 checked
获取用户选择结果:用 name 查找,不是遍历所有 radio
提交前想校验或读取选中值,最简单可靠的方式是用 document.querySelector('input[name="q3_preferred_language"]:checked')。它直接返回当前被选中的那个元素,没选中就返回 null,不用手动遍历、判断 .checked。
容易踩的坑:
- 写成 document.querySelectorAll('input[type="radio"]') 然后循环查 .checked,效率低且易漏逻辑
- 忘记加 :checked 伪类,导致拿到的是全部 radio 元素
- 在表单 submit 事件里用 event.target.elements['xxx'] 取值时,注意它返回的是 RadioNodeList,需访问 .value(不是 .checked)才能拿到选中项的 value
无障碍与样式适配不能只靠 CSS
纯用 <input type="radio"> + <label></label> 结构,默认具备键盘导航(Tab 切换)、空格选中、屏幕阅读器支持等基础无障碍能力。但如果为了视觉效果完全隐藏原生 input(比如 display: none 或 opacity: 0),又没补全 ARIA 属性(如 aria-checked、role="radio"),就会切断语义链。
稳妥做法:
- 保留原生 <input>,用 CSS 视觉隐藏但保持可访问(例如 position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;)
- 所有自定义样式都作用于关联的 <label></label> 或其伪元素
- 不要删掉 for 或 id,否则点击 label 无法聚焦 input
实际问卷中,name 命名冲突、默认值遗漏、无障碍断层这三处最容易在线上环境暴露问题,尤其在多页问卷或动态渲染场景下。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











