HTML的<input type="radio">是替换元素,不支持伪元素;需隐藏原生控件并用关联<label>的::before/::after模拟样式,同时保留可访问性与焦点能力。

单选按钮默认样式无法用伪元素直接覆盖
HTML 的 <input type="radio"> 本身不支持 ::before 或 ::after,因为它是替换元素(replaced element),且原生不可见内容区域。直接对 input[type="radio"] 写伪元素无效——浏览器会忽略,连 DevTools 都不显示。
真正能生效的方案是:隐藏原生 radio,用其后的 <label>(或相邻兄弟元素)承载伪元素视觉表现。
- 必须将
input置为position: absolute; opacity: 0; width: 0; height: 0;或clip: rect(0 0 0 0);(后者更稳妥,避免焦点/可访问性问题) -
label需与input关联(for="id"或包裹input),否则点击无效 - 伪元素必须加在
label上,且需设置position: relative;才能精确定位
用 ::after 绘制圆形选中标记的典型写法
核心是利用 label::after 模拟“选中态圆点”,通过 input:checked + label::after 控制显隐。注意:选择器必须是相邻兄弟(+)或通用兄弟(~),不能跨父容器。
input[type="radio"] {
clip: rect(0 0 0 0);
position: absolute;
}
input[type="radio"] + label {
position: relative;
padding-left: 28px;
cursor: pointer;
}
input[type="radio"] + label::before {
content: "";
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 18px;
height: 18px;
border: 2px solid #999;
border-radius: 50%;
}
input[type="radio"]:checked + label::after {
content: "";
position: absolute;
left: 5px;
top: 50%;
transform: translateY(-50%);
width: 8px;
height: 8px;
background: #007bff;
border-radius: 50%;
}
关键细节:
立即学习“前端免费学习笔记(深入)”;
-
label::before是未选中时的外圈,label::after是选中时的内点,两者用不同层级和尺寸区分 -
top: 50%; transform: translateY(-50%)是垂直居中的可靠写法,比top: 5px更健壮 - 若 label 文字行高变化,
transform仍能保持居中;硬写像素值容易错位
无障碍与键盘焦点常被忽略的坑
仅靠伪元素视觉反馈,屏幕阅读器和键盘用户可能完全感知不到当前选中项。原生 input 被 clip 后仍需保有可聚焦、可读性能力。
- 绝不能用
display: none或visibility: hidden隐藏input,这会让它彻底脱离可访问树 - 必须保留
tabindex="0"(如果用了clip,通常已足够;但某些旧版 Safari 需额外加tabindex) - 建议为
label加user-select: none;,防止双击误选文字干扰操作 - 可通过
input:focus + label::before添加焦点环样式,提升键盘导航体验
IE11 及部分安卓 WebView 对 :checked + label 的兼容性问题
IE11 支持 :checked,但某些低版本安卓系统(如 Android 4.4 WebView)对相邻兄弟选择器 + 匹配不稳定——尤其当 DOM 中存在注释节点或文本节点时。
- 确保
input和label是紧邻兄弟,中间无空格、换行或注释 - 更稳妥的写法是把
input套进label内:<label><input type="radio" name="x">选项</label>,然后用input:checked + label::after改为input:checked::after(不行)→ 实际应改用label input:checked + span::after或直接用label::after配合 JS 切 class - 如需强兼容,放弃纯 CSS 方案,监听
change事件切换label的data-checked属性,再用属性选择器控制伪元素
伪元素绘制标记这事,看着简单,但一动真格就卡在可访问性和老环境上——样式可以重绘,语义和交互链断了,用户就真的“看不见”了。



















