用 input[type="radio"] 实现星级评分可确保键盘导航、屏幕阅读器播报和语义化;需共享 name、配合 label、隐藏 input 但保留可访问性、用 svg 图标、监听 change 事件、校验值范围、正确处理禁用状态。

用 input[type="radio"] 实现可点击、可聚焦、无障碍的星级评分
纯 CSS + 语义化 HTML 就能做出真正可用的星级组件,关键不是“画五颗星”,而是让每颗星能被键盘 Tab 切入、Space 或 Enter 选中,且屏幕阅读器能正确播报“当前选中 3 颗星”。input[type="radio"] 天然支持这些能力,比用 div + click 事件靠谱得多。
实操建议:
- 一组
input[type="radio"]必须共享同一个name属性(如name="rating"),否则无法互斥选择 - 每个
input后紧跟一个label,用for绑定id,或直接包裹input—— 这样点击文字/图标都能触发 - 隐藏原生
input用position: absolute; opacity: 0;,别用display: none或visibility: hidden,否则会脱离可访问性树 - 用
:checked ~ .star-icon或input:checked + label控制后续兄弟元素样式,实现“点第3颗,前3颗高亮”
用 font-awesome 或 SVG 图标替代 Unicode 星号,避免字体加载失败导致空白
很多人直接用 ★ ☆ 字符,看似简单,但字体缺失、编码错误、缩放模糊时体验极差。更稳的方式是用矢量图标。
实操建议:
- 若用
font-awesome,确保引入完整 CSS,并给label加class="fa fa-star";注意 v6 默认是svg-inline模式,需额外配置才能支持伪元素 - 更推荐内联 SVG:每个星用
<svg><path></path></svg>,填色用fill控制,完全不依赖外部资源 - 不要对图标用
font-size缩放 SVG,应设width/height并加viewBox保证清晰度 - 已选中状态用
fill="#ffc107",未选中用fill="#e9ecef",符合 WCAG 对比度要求
监听 change 事件而非 click,避免重复触发和键盘操作失效
用户用空格键选中某颗星时,click 事件不会触发,但 change 一定会。只监听 click 会导致键盘用户无法提交评分。
实操建议:
- 绑定事件到整个 radio 组的容器上,用事件委托:
container.addEventListener('change', e => { if (e.target.matches('input[type="radio"]')) { ... } }) - 获取值直接读
e.target.value(建议设为"1"~"5"字符串),别去数第几个被选中 - 如果需要实时预览(比如旁边显示“你评了 4 星”),在
change回调里更新文本内容即可,无需防抖 - 服务端接收时,务必校验该值是否在
["1","2","3","4","5"]范围内,前端控制不可信
禁用状态要同时处理视觉、交互和语义三层
仅加 disabled 属性还不够:图标灰度不够、鼠标指针仍是手型、屏幕阅读器可能仍播报“可选中”。
实操建议:
- 给容器加
aria-disabled="true",并配合tabindex="-1"防止键盘聚焦 - CSS 中用
.rating[aria-disabled="true"] .star-icon { opacity: 0.5; cursor: not-allowed; } - 禁用时移除所有
input的checked属性,避免残留选中态 - 如果后端返回只读评分(如查看他人评价),改用
fieldset[disabled]包裹整组,比单个input disabled更语义化
Tab 进入组件后,按方向键无法切换星号,或者回车没反应。这往往是因为用了 div 模拟 radio 却没补全 role="radio"、aria-checked 和按键响应逻辑。用原生 input[type="radio"] 是最省心的起点。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











