如何用 fieldset 和 legend 组织复杂表单并提升可访问性
把相关表单控件用 <fieldset> 包起来、用 <legend> 给这组控件起个名字,是 HTML 原生里代价最低的可访问性改造:屏幕阅读器(NVDA、JAWS、VoiceOver 均支持)读到一个分组内的控件时,会把 legend 文本当作分组名播报,用户每进入一组就知道自己正在填什么。
fieldset 和 legend 到底解决了什么问题?
结论:<fieldset> 负责"分组",<legend> 负责"给这组命名",两者合起来在可访问性树里形成 role="group" + 可访问名称。
不用它的时候,三个 type="radio" 的选项只会被读成"标准配送""次日达""自提",用户听不出这三个选项是在回答哪个问题——选项本身没有上下文。加上 legend 后,读屏会先播报"配送方式,分组",再逐个读选项。
这条规则不只适用于单选组。支付信息、收货地址、发票信息、日期时分秒三段输入,凡是"若干控件共同回答一个问题"的结构,都该用 fieldset + legend 包起来。
怎么写一个可访问的单选组?
结论:legend 写问题,label 写选项,每个选项只描述自己,不要重复组名。
<fieldset>
<legend>配送方式</legend>
<label><input type="radio" name="ship" value="std"> 标准配送(3–5 天)</label>
<label><input type="radio" name="ship" value="exp"> 次日达</label>
<label><input type="radio" name="ship" value="pickup"> 门店自提</label>
</fieldset>
注意三点:legend 不要写"请选择配送方式,标准配送需要 3–5 天"这种长句,它会被反复播报;每个 label 的文本要能独立听懂;name 属性必须相同,否则不会互斥。
为什么 legend 必须是 fieldset 的第一个子元素?
结论:legend 是 fieldset 的标题元素,只有放在第一位,浏览器才会把它参与渲染和分组名称计算。
HTML 规范把 legend 定义为 fieldset 的 caption。如果你写成:
<fieldset>
<p>配送方式</p>
<legend>配送方式</legend>
</fieldset>
部分浏览器会直接忽略掉那个不在首位的 legend,可访问性名称就丢了。哪怕你用 CSS 把 legend 隐藏起来视觉上重排,也请保持它在 DOM 里的第一个位置。
fieldset 默认样式为什么会让布局撑破?怎么重置?
结论:fieldset 默认带 min-inline-size: min-content,放进 flex 或 grid 容器里会拒绝收缩,导致横向溢出;重置时要把它设成 0。
浏览器对 fieldset 的默认样式大致是:border: 2px groove、padding: 0.35em 0.75em 0.625em、margin-inline: 2px,外加上面那条 min-inline-size。常见的重置写法:
fieldset {
border: 0;
padding: 0;
margin: 0;
min-inline-size: 0; /* flex/grid 里防止撑破 */
}
legend {
padding: 0;
font-weight: 600;
}
另外,legend 的样式能力有限:它在部分浏览器里不能被随意设置 margin、padding 或参与绝对定位的父级裁剪,所以别把它当普通块级元素做复杂排版,需要视觉自由就把样式做在 fieldset 上,legend 只保留文字。
错误提示怎么和分组关联?
结论:分组级错误不要挂在某个具体控件上,用 aria-describedby 从 fieldset 指过去。
<fieldset aria-describedby="ship-err">
<legend>配送方式</legend>
<!-- 三个 radio -->
</fieldset>
<p id="ship-err" role="alert">请先选择配送方式</p>
aria-describedby 加在 fieldset 上,读屏在进入分组时会连同错误描述一起播报;role="alert" 让提示在动态插入时被立即朗读。如果把错误挂到第一个 radio 上,用户选中其他选项时就听不到。
什么时候不该用 fieldset?
结论:单个输入框不要用;嵌套不要超过两层。
一个"邮箱"输入框配一个 label 就够了,套 fieldset 只会让读屏多播报一次"分组"。同理,fieldset 可以嵌套,但每进一层读屏就多播报一个组名,三层以上会让用户迷失——常见做法是最多两层,比如"收货地址"(外层)里套"发票抬头"(内层)。另外,fieldset 加 disabled 会一次性禁用组内全部表单控件,适合"整块暂不可填"的场景,但注意 disabled 的控件不会随表单提交,也不参与校验。
总结:legend 放第一位、写清组名,fieldset 在 flex 布局里补 min-inline-size: 0,错误提示用 aria-describedby 挂在分组上,只在"多个控件回答同一个问题"时使用——这四条做到,复杂表单的可访问性基本就过关了。
原文链接:https://www.gj0.com/thread-901.html
转载请注明出处并保留本声明;内容仅代表作者观点,与本站立场无关。若本文涉嫌侵权,请联系本站处理。