单选题必须用<fieldset>包裹,<legend>写题干,选项用同name的radio输入框;多选题name加[]后缀;需<label>扩展点击区并正确绑定;提交验证须JS辅助。

单选题怎么写才不会被屏幕阅读器误读
单选题必须用 <fieldset> 包裹,<legend> 写题干,每个选项用带相同 name 的 <input type="radio">。否则视障用户无法感知题目范围,浏览器也可能把多个单选组识别成独立控件。
常见错误:直接堆砌 <input type="radio">,没 <fieldset> 和 <legend>,或者 name 值写错(比如大小写不一致、空格多一个),导致无法互斥选择。
-
<legend>必须是<fieldset>的第一个子元素,不能用<div>或<p>替代 - 所有同组单选按钮的
name值必须完全一致(包括大小写和空格) - 每个
<input>必须配<label>,且用for属性绑定id,别只靠视觉位置对齐
<fieldset> <legend>浏览器默认字体大小是多少?</legend> <label><input type="radio" name="q1" value="16px" id="q1-a"> 16px</label> <label><input type="radio" name="q1" value="14px" id="q1-b"> 14px</label> </fieldset>
多选题的 value 设计影响后端接收逻辑
多选题本身没有语义化容器要求,但 name 后缀加 [](如 name="topics[]")是 PHP/Node.js 等后端解析数组的通用约定;若漏掉,后端通常只收到最后一个勾选项。
更隐蔽的问题是:前端没做任何校验时,用户可能一个都不选——这会导致后端收到空数组或 undefined,而很多表单验证逻辑默认只检查非空字符串。
立即学习“前端免费学习笔记(深入)”;
- 务必给每个多选项设置明确的
value,别留空或用中文(易编码出错) - 如果后端是 Python Flask/Django,
name="topics"也能接收列表,但需调用getlist()而非get() - 避免用
disabled代替隐藏选项,它会让该值完全不提交;要用hidden输入框或 JS 控制显隐
“提交”后怎么知道用户选了什么
纯 HTML 表单提交后页面刷新,你无法在前端直接读取用户选择——除非用 JS 拦截 submit 事件,或用 <form method="get"> 让答案出现在 URL 里(适合调试)。
常见误操作:以为给 <input> 加 required 就能阻止无选择提交,其实它只对单个控件生效;单选组中任意一项被选中即算通过,多选组则没有原生“至少选 N 项”的约束。
- 单选组加
required是有效的,但必须确保至少一个<input>没有disabled或checked属性干扰 - 多选组要“至少选两项”,只能靠 JS 监听
change+ 手动统计querySelectorAll('input:checked').length - 不要依赖
document.forms[0].elements遍历取值——<fieldset disabled>里的控件会被忽略,得用form.querySelectorAll('input:checked')
移动端点击区域太小导致误操作
原生 <input type="radio/checkbox"> 在 iOS 和 Android 上点击热区只有 12×12px 左右,手指稍偏就点不中。这不是 bug,是规范行为——所以必须用 <label> 扩展可点区域。
容易被忽略的是:当 <label> 包裹 <input> 时,CSS 的 pointer-events: none 或 opacity: 0 若加在 <input> 上,会连带让整个 <label> 失效。
- 推荐写法:
<label><input type="radio"> 选项文本</label>,而非用for/id分离 - 给
<label>加display: block和padding,比调<input>的zoom或transform更可靠 - 别用
font-size: 0隐藏 input——它在部分安卓 WebView 中会破坏焦点逻辑



















