:defined伪类仅在自定义元素完成customElements.define()注册并升级后匹配,不可用于表达“加载中”;必须用:not(:defined)配合display和最小尺寸做占位,否则导致FOUC或布局偏移。

:defined 伪类本身不处理“加载状态”,它只反映自定义元素是否已完成 customElements.define() 注册并升级。所谓“加载中”的视觉反馈,必须靠 :not(:defined) 配合显式占位样式来实现,否则用户看到的是 inline 裸标签、塌陷布局或空白闪烁。
为什么 :defined 不能直接表达“加载中”
:defined 是一个二值信号:匹配 = 已注册+已升级;不匹配 = 未注册 / 注册失败 / 标签名非法 / CSS 加载过早。它不感知网络请求、脚本下载、组件初始化逻辑或内部 connectedCallback 执行进度。你写 my-dialog:defined { opacity: 1; },只是告诉浏览器“等它就绪了再应用”,但无法控制“就绪前怎么看起来像在加载”。
- 常见错误现象:只写
my-card:defined样式,没写:not(:defined)占位 → 页面首屏闪出一堆窄高为 0 的,然后突然撑开,造成 layout shift - 根本原因:未定义的自定义元素默认是
display: inline,无视width、height、background等块级属性 - 正确理解:
:not(:defined)不是“加载中伪类”,而是“未就绪降级占位必需项”
:not(:defined) 必须配 display 和最小尺寸
只写 my-card:not(:defined) { background: #eee; } 没用——背景不会显示,因为元素仍是 inline。必须先把它变成可布局的盒子。
-
my-card:not(:defined) { display: block; min-height: 48px; }是底线配置 - 如需骨架屏效果,可加
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); background-size: 200% 100%; animation: loading 1.5s infinite; - 避免用
visibility: hidden或opacity: 0:它们不占空间,会导致后续内容上移,重排更剧烈 - 务必把
:not(:defined)规则写在:defined规则之前,否则后者权重相同会覆盖前者
注册时机错位导致 :not(:defined) 失效
CSS 解析是同步的,JS 注册是异步的。如果 HTML 中已存在 ,但 customElements.define('my-button', ...) 在
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











