纯HTML+CSS无法真正实现多规格联动校验,必须用JS动态禁用无效选项;正确做法是每规格用fieldset包裹、radio单选+label增强点击区,通过监听change事件查SKU数据并设置disabled与aria-disabled。

怎么用纯HTML+CSS实现多规格商品属性选择卡片
直接上手就能用,不需要框架或JS也能完成基础交互(靠input[type="radio"] + :checked伪类),但真要支持「已选规格组合校验」「禁用无效选项」就得加少量JS。纯静态方案适合原型、后台管理页或SSG生成页。
常见错误是把所有规格选项平铺成独立单选组,导致用户可能选到不存在的SKU组合(比如“红色+XL”实际缺货,但页面仍允许勾选)。正确做法是让每组规格的可选项动态响应其他已选项——这一步必须用JS控制disabled状态。
- 每个规格维度(如颜色、尺寸)用一个
fieldset包裹,含legend说明名称 - 每个选项用
label包住input[type="radio"],保证点击文字也能触发 - 给
input加name属性(如name="color"),同组单选互斥 - 用
data-sku-id或data-combination标记每个选项对应的有效组合,供JS校验
为什么disabled不能只靠HTML初始状态
后端返回的SKU列表通常是扁平数组,例如[{"color":"红","size":"S","stock":10},{"color":"蓝","size":"M","stock":0}]。你无法在HTML里预先写死哪些input该禁用——因为禁用逻辑依赖「当前已选其他规格」。比如:选了“红”之后,“XL”才需要禁用(如果红XL无库存),但初始时“XL”是可用的。
所以必须监听change事件,遍历所有已选值,查SKU表,再批量设置disabled。注意别用click,避免重复触发;也别在每次选中都全量重渲染DOM,只需切换disabled和aria-disabled属性即可。
立即学习“前端免费学习笔记(深入)”;
- 监听范围锁定在规格
fieldset内,避免全局事件污染 - 用
Array.from(document.querySelectorAll('input[type="radio"]:checked'))快速取当前选中项 - 禁用时加
aria-disabled="true",兼顾可访问性 - 清空选择时,需重置所有
disabled状态,否则残留禁用会误导用户
input[type="radio"]和input[type="checkbox"]在规格选择里怎么选
绝大多数多规格场景用radio——颜色、尺寸、版本这些是单选维度。只有极少数情况用checkbox,比如「配件加购」(蓝牙耳机+充电盒可单独勾选),但它不属于主规格,应和主规格卡片分离。
混淆点在于:有人想用checkbox实现「多选一」,结果发现无法强制单选,还得自己写JS限制数量,反而增加复杂度。记住一条:只要一个维度只能选一个值,就用radio;它的语义和浏览器原生行为就是为这个设计的。
-
radio天然保证同name下仅一个选中,无需额外校验 -
checkbox若强行模拟单选,焦点管理、键盘操作(Tab+Space)会出问题 - 移动端触控区域小,
radio配label后点击热区更大,体验更好
怎么让选中态视觉反馈更清晰
默认的radio样式丑且不统一,尤其在深色背景或高对比度模式下几乎看不见。不要用appearance: none后从零画圆圈——容易忽略焦点状态和屏幕阅读器支持。
推荐用input[type="radio"] + label::before做定制,但必须保留input本身(用于表单提交和无障碍),并确保:focus样式足够明显。iOS Safari对::after在label里的支持不稳定,统一用::before更稳妥。
- 用
border和background模拟选中圆点,别用box-shadow(部分安卓机不渲染) - 选中时加
transform: scale(1.2)比单纯变色更易感知 - 禁用状态用
opacity: 0.5+cursor: not-allowed,别只改颜色 - 测试时打开系统「减少动画」开关,确认缩放动画是否被禁用
真实项目里最常被跳过的环节是SKU组合实时更新价格和库存文案——它不改变卡片结构,但用户会盯着那行字看。这部分逻辑得和规格选择强绑定,别等「加入购物车」时才校验。



















