截至2026年7月,:hover、:focus、:active、:visited、:checked、:disabled、:valid、:invalid、:defined、:has()已获主流浏览器支持,而:state()仍为草案且无实现;自定义状态必须通过data-属性配合JS驱动,:has()是唯一支持跨层级状态联动的伪类。

直接用伪类选择器就行,比如 :hover、:focus、:checked,但必须清楚哪些状态是浏览器原生支持的、哪些只是草案或需要 JS 配合。
哪些伪类能直接用,哪些不能信
截至 2026 年 7 月,:hover、:focus、:active、:visited、:checked、:disabled、:valid、:invalid、:defined、:has()(Chrome/Firefox/Safari 已支持)都是真实可用的;而 :state() 仍是 CSSWG 草案,没有任何浏览器实现,写上去完全无效。
常见误判场景:
- 看到 demo 里
my-input:state(error)生效了?大概率是 demo 用了data-state="error"+ 属性选择器,不是伪类 - 在组件里调用了
internals.states.add('loading'),却没同步设置data-state,结果样式毫无反应 - 把
:defined当成状态监听器——它只管“是否注册”,不管“当前值”,注册完就永远匹配
:has() 是唯一能跨层级响应状态的真·伪类
:has() 允许你根据子元素或兄弟元素的状态,给父级或前序元素加样式,是目前最接近“状态联动”的原生方案。
立即学习“前端免费学习笔记(深入)”;
典型用法:
-
form:has(input:invalid)给整个表单加红框提示 -
label:has(input:checked)让 label 跟随 checkbox 状态变色 -
nav:has(.menu-toggle:checked) .mobile-menu控制菜单显隐(无需 JS)
注意::has() 不能出现在选择器最右端(如 div:has(span):hover 合法,div:hover:has(span) 不合法),且性能敏感,避免嵌套过深或高频触发。
自定义状态必须靠 data- 属性驱动
想让外部 CSS 响应自定义元素内部逻辑(比如“加载中”“展开中”),唯一可靠路径是:JS 控制状态 → 同步写入 data- 属性 → CSS 用属性选择器匹配。
实操建议:
- 组件内状态变更时,用
this.setAttribute('data-state', 'loading')或this.removeAttribute('data-state') - CSS 写成
my-dialog[data-state="open"] .overlay { opacity: 0.8; } - 多状态并存时用空格分隔:
data-state="loading disabled",再用[data-state~="loading"]匹配 - 别用
class模拟状态——class 易被用户、框架、CSS-in-JS 覆盖,data-更专一、可控
伪类生效的前提常被忽略
很多样式不生效,不是写错了选择器,而是没满足触发条件:
-
:focus只对可聚焦元素默认生效(input、button、a),div需加tabindex="0" -
:checked仅适用于input[type="checkbox"]和input[type="radio"],span或自定义标签加了也无效 -
:hover在触摸设备上行为不稳定,iOS Safari 默认延迟 300ms,且不支持“悬停即触发”,需配合@media (hover: hover)做降级 -
:has()中的子选择器若含伪类(如:focus),该伪类本身也得满足触发条件才能连锁生效
真正难的不是写对伪类,而是判断当前上下文是否允许它进入那个状态——状态存在,不代表浏览器会感知到它。


















