:defined 伪类仅在自定义元素完成注册、解析与升级三阶段后生效,非声明即匹配;常见失效原因为脚本执行晚于html解析或注册失败,需确保 customelements.define() 提前执行并检查控制台错误。

`:defined` 伪类什么时候会生效
它只在自定义元素(customElements.define())调用成功后才匹配对应标签,不是“声明了 class 就算”,也不是“写了 <my-button></my-button> 就立刻触发”。浏览器必须完成注册、解析、升级三个阶段,:defined 才从假变真。
常见错误现象:<my-input></my-input> 在 DOM 中已存在,但样式没生效——大概率是脚本执行晚于 HTML 解析,元素还没被升级;或者注册失败(比如重复定义、构造函数非法),customElements.define() 抛错但被静默吞掉。
- 确保
customElements.define()在元素插入 DOM 前执行,或至少在DOMContentLoaded后立即执行 - 检查控制台是否有
Failed to execute 'define' on 'CustomElementRegistry'类错误 - 用
customElements.get('my-input')手动查注册状态,返回function才算成功
`:defined` 和 `:not(:defined)` 的实际用途
最实在的用法就两个:加载占位态控制、避免未定义元素闪现异常样式。比如你希望 <chart-widget></chart-widget> 在定义前完全透明,定义后才显示边框和阴影,就得靠 :not(:defined) 锁住初始态。
不能用来做功能降级(比如“没定义就 fallback 成 <div>”),因为伪类不改变 DOM 结构,也不触发 JS 逻辑;它只是 CSS 层的视觉开关。<p><span>立即学习</span>“<a href="https://pan.quark.cn/s/cb6835dc7db1" style="text-decoration: underline !important; color: blue; font-weight: bolder;" rel="nofollow" target="_blank">前端免费学习笔记(深入)</a>”;</p>
<ul>
<li>
<code>my-button:not(:defined) { visibility: hidden; } —— 安全,无布局偏移
my-button:not(:defined) { display: none; } —— 风险高,可能造成页面跳动:defined 做复杂动画,升级时机不可控,容易和 transition 冲突兼容性与 SSR 场景下的表现
:defined 在所有现代浏览器中都可用(Chrome 67+、Firefox 63+、Safari 15.4+),但 Node.js 环境或静态渲染(如 Next.js SSG、Vue SSR)里它永远不匹配——服务端没有 customElements 注册机制,:defined 永远为假。
这意味着:纯服务端渲染时,:not(:defined) 规则会一直生效,可能导致首屏内容不可见。必须配合 JS 激活逻辑或服务端注入占位策略。
- SSR 项目中,给自定义元素加
is="undefined"属性,用[is='undefined']替代:not(:defined) - Hydration 后手动触发一次
customElements.upgrade(document.body),加速未升级元素处理 - 不要依赖
:defined控制关键可访问性属性(如aria-hidden),它不具备语义穿透力
为什么不能用 JavaScript 直接监听“定义完成”
没有全局事件告诉你“某个元素类型刚刚被 define”,customElements.define() 是同步操作,但元素升级(upgrade)是异步的,且发生在元素被插入文档时。你无法准确预测哪个时刻该元素真正“可用”。
所以 :defined 的价值恰恰在于它把这种不确定性交给了 CSS 引擎处理——你只要写好规则,浏览器会在每次样式计算时自动重判,无需手动轮询或打补丁。
- 别写
while (!customElements.get('x-foo')) {},会阻塞主线程 - 别在
connectedCallback里反复改className来模拟:defined,破坏语义且难维护 - 如果真需要 JS 响应,用
MutationObserver监听新增元素,再结合customElements.get()判断是否已注册
最常被忽略的一点:伪类匹配不触发重排,但频繁插入/移除大量未定义自定义元素,仍可能让浏览器反复尝试升级,带来隐性性能开销。上线前用 Performance 面板抓一下 CustomElementUpgrade 事件频次。










