原生HTML无toggle标签,开关必须用checkbox配合CSS实现;button无法提供表单语义与无障碍支持;需用label包裹、隐藏checkbox、伪元素绘制滑块,并添加role="switch"与同步更新的aria-checked属性。
原生 HTML 没有 <input type="toggle">,所谓“开关按钮”必须用 <input type="checkbox"> 配合 CSS 实现——别试图找内置 toggle 标签,它不存在。
<button> 模拟开关状态用 <button> 手动切换 class 或 textContent 虽然能视觉上“切换”,但会丢失表单语义、无障碍支持(如屏幕阅读器无法识别开/关状态)、提交时无法自动携带值。真正需要开关行为的场景(比如用户偏好设置、API 开关配置),必须用 <input type="checkbox"> 作为底层控制源。
<input type="checkbox"> 的 checked 属性天然表达二元状态,且可被 form 自动序列化aria-checked, role="switch")都依赖这个基础元素change 事件比监听 click 更可靠(避免空格键、Enter 键等触发失效)核心是隐藏原生 checkbox,用 ::before/::after 伪元素画滑块和轨道。不依赖 JS,纯 CSS 即可响应状态变化。
<label class="switch"> <input type="checkbox"> <span class="slider"></span></label>.switch input { display: none; }.slider { position: relative; display: inline-block; width: 52px; height: 26px; background-color: #ccc; border-radius: 26px; transition: .2s;}.slider::before { content: ""; position: absolute; left: 2px; top: 2px; width: 22px; height: 22px; background-color: white; border-radius: 50%; transition: .2s;}.switch input:checked + .slider { background-color: #4CAF50;}.switch input:checked + .slider::before { transform: translateX(26px);}
<label> 包裹 <input> 和视觉元素,点击任意区域都能触发 checkboxdisplay: none 隐藏 checkbox 后再用 visibility: hidden 补救——这会让部分屏幕阅读器跳过该控件prefers-color-scheme 媒体查询控制背景色,而非 JS 切换 class仅靠视觉样式无法满足 WCAG 2.1 AA 级要求。当使用 role="switch" 时,aria-checked 必须与 checkbox 的 checked 属性同步——浏览器不会自动同步,得靠 JS 维护。
立即学习“前端免费学习笔记(深入)”;
<label class="switch"> <strong>启用通知</strong> <input type="checkbox" id="notify-toggle"> <span class="slider" role="switch" aria-checked="false" aria-labelledby="notify-toggle"></span></label>
role="switch" 必须加在视觉滑块容器(即 .slider)上,不能加在 <input> 上aria-checked 初始值要和 <input> 的 checked 属性一致;后续用 input.addEventListener('change', () => { el.setAttribute('aria-checked', this.checked) }) 更新复杂点在于:开关的“开启/关闭”文案是否要随状态动态变化?如果需要,不能只改 textContent,还得同步更新 aria-label,否则屏幕阅读器会读错。这点常被忽略,但直接影响视障用户操作信心。