必须用做开关,因其具备原生:checked状态、表单自动提交、空格键切换及无障碍支持;无此能力,仅视觉模拟会导致状态不同步、键盘不可操作、屏幕阅读器误读且后端收不到值。

不能用 <button></button> 做开关 —— 它没有原生状态同步能力,也无法被表单自动提交或键盘空格键切换。 真正可用的开关必须基于 <input type="checkbox">,否则就是“假开关”:视觉像、行为错、无障碍失效、后端收不到值。
为什么 <button></button> 不能替代 <input type="checkbox">
很多人想用 <button></button> + JS 手动 toggle class 来模拟开关,但会立刻遇到几个硬伤:
-
button没有:checked伪类,CSS 动画无法直接响应状态变化,必须靠 JS 干预 DOM 类名,代码变重、易不同步 - 按 Tab 键聚焦到
button后,按空格键不会切换状态(除非手动监听并调用click()),违反 WCAG 键盘可访问性要求 - 放在
<form></form>里时,button默认不参与序列化提交;即使加name和value,也无法表达“开/关”二元状态(它只在type="submit"时才发值) - 屏幕阅读器读出的是“按钮”,不是“复选框”,用户无法感知当前是“已启用”还是“已禁用”
label 和 input 的关联方式怎么写才稳
最可靠的是包裹结构:<label><input type="checkbox" id="theme-toggle"></label>。这样不用操心 for/id 匹配错误,也不怕中间插入其他元素导致选择器失效。
- 如果非要用
for关联,确保<input id="theme-toggle">和<label for="theme-toggle"></label>的字符串完全一致(大小写、空格、连字符都不能差) -
label必须有明确尺寸或display: inline-block,否则伪元素定位会漂移 - 给
label加cursor: pointer,让用户一眼看出可点 - 别给
label写user-select: none以外的交互限制,否则长按可能触发复制菜单,尤其在 iOS 上
隐藏原生控件的正确姿势
用 display: none 或 visibility: hidden 都不行 —— 前者让键盘焦点失效,后者仍占布局空间且焦点不可见。
- 推荐组合:
position: absolute; opacity: 0; pointer-events: none; - 或者更稳妥的裁剪法:
position: absolute; clip: rect(0 0 0 0); - 绝对不要删掉
input元素本身,它是状态唯一信源;所有动画、JS 监听、表单提交都依赖它 - 检查是否保留了
tabindex="0"(不需要加,原生input默认可聚焦)
滑块位移必须用 transform: translateX()
写 left: 26px 看似简单,但会触发 layout → paint → composite 流程,动画卡顿;translateX() 只走 composite,GPU 加速,iOS Safari 也跑得顺。
- 轨道宽 52px、高 26px 时,滑块宽高为 26px,未选中时
transform: translateX(0),选中时transform: translateX(26px) - 过渡必须写在伪元素上(如
label::after),而不是input:checked上——后者不渲染任何东西 - 动画时间控制在
0.2s,贝塞尔曲线用cubic-bezier(0.55, 0, 0.55, 0.95),别简写成ease-in-out - 移动端点击热区至少 44×44px,靠
label的padding或min-height/min-width扩展,别只靠伪元素大小
最难绷住的其实是尺寸对齐:滑块位移量 = 轨道宽度 − 滑块宽度,差 1px 就会卡边或露白。别抄网上的固定像素值,拿设计稿量,再用 calc() 或 CSS 变量算准。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











