:checked伪类失效最常见原因是radio被display:none或visibility:hidden隐藏,应改用position:absolute;opacity:0;宽高1px安全隐藏;name属性大小写或空格不一致导致分组失效;JS覆盖初始checked状态。

为什么:checked 伪类样式完全没反应
最常见原因是原生 <input type="radio"> 元素被错误地用 display: none 或 visibility: hidden 隐藏了。这两种方式会直接切断 :checked 的状态映射:浏览器无法渲染伪类,屏幕阅读器也读不到选中态。
正确做法是保留 input 在 DOM 中,用 position: absolute; opacity: 0; width: 1px; height: 1px; overflow: hidden; 安全隐藏——它既不破坏可访问性,又能让 :checked + label 或 label[for] 关联的伪类正常触发。
- 别写
input[type="radio"] { display: none; }—— 这是失效根源 - 若用了
appearance: none,务必补全所有旧版 Safari/Android WebView 的前缀(-webkit-appearance: none;),但依然建议放弃该方案 - 检查是否误将
:checked写在了label上(如label:checked),正确写法只作用于input元素本身或其后兄弟元素
name 属性不一致导致单选组“不互斥”
浏览器只靠 name 值做分组,大小写、空格、不可见字符全算差异。哪怕一个写 name="theme",另一个写 name=" theme"(开头多空格),它们就是两组独立控件,选中不会取消彼此。
调试时打开 Elements 面板,逐个点开 radio 元素,看 name 属性值是否**完全一致**(复制粘贴比对)。服务端接收时也依赖这个字段,前后端必须对齐。
立即学习“前端免费学习笔记(深入)”;
- 同一组 radio 必须共用且仅共用一个
name值,不能靠 class 或 id 模拟分组 - 动态插入的 radio(如 JS 创建),需确保
name字符串与已有项严格相同,避免拼接时引入空格或换行 - Vue/React 中用 v-bind:name 或 :name 绑定时,确认变量值无首尾空格(可用
.trim()防御)
默认 checked 不生效,但 DOM 看起来是对的
HTML 中写 <input type="radio" name="size" value="m" checked> 是对的,但若页面加载后 JS 又执行了 document.querySelector('[name="size"]').checked = false 或类似重置逻辑,初始状态就被覆盖了。
更隐蔽的情况是:表单被包裹在框架组件里(如 React 的受控组件),value 由 state 驱动,此时 HTML 的 checked 属性会被忽略,必须同步管理 state。
- 不要写
checked="true"或checked="checked",布尔属性存在即生效,多余值反而可能在某些解析器中引发歧义 - 用 DevTools 的 Elements 面板刷新后立刻查看元素,确认
checked属性是否真实存在于 HTML 中(而非仅 JS 动态添加) - 如果用了
form.reset(),它会强制还原到初始 HTML 状态,但前提是初始状态本身没被 JS 干扰过
自定义样式在移动端或旧浏览器中失效
移动端 Safari 和部分 Android WebView 对 :checked ~ .custom-dot 这类通用兄弟选择器支持不稳定,尤其当结构嵌套较深时。更稳妥的是用 :checked + label(相邻兄弟)并确保 label 紧跟 input 后面。
另外,:hover 在真机上基本不触发,别指望靠它模拟选中高亮;要用 :focus 或 :active 补充键盘和触摸反馈。
- 避免深层嵌套:把
input和关联label放在同一层 DOM,不要隔div或span - 测试时禁用所有 JS,纯靠 HTML+CSS 验证基础交互是否成立,再逐步加 JS 增强
- 若必须兼容 iOS 12 以下,考虑 fallback 到 JS 监听
change事件来手动切换 class
name 多了一个空格、input 被 display: none 锁死、或者 JS 在 DOM ready 前就执行了 reset。先盯死这三处,90% 的问题当场消失。



















