:defined用于判断自定义元素是否已注册但未升级完成,需满足三前提:含短横线标签名、已被customelements.define注册、css在body内加载;:not(:defined)须配display才生效;:state()不被支持,应改用data-属性配合js钩子。

:defined 不是用来“检测是否加载”,而是判断浏览器是否已执行 customElements.define() —— 它匹配的是**已注册、尚未升级完成的自定义元素**,不是脚本是否下载完、是否执行到 define 行,更不是组件是否渲染完毕。
为什么 :not(:defined) 样式不生效?三个硬性前提必须同时满足
它只对合法自定义元素起作用,缺一不可:
- 标签名含短横线(如
<my-button></my-button>✅,<mybutton></mybutton>❌,<button-x></button-x>✅,<div> ❌)<li>该标签名已被 <code>customElements.define('my-button', ...)注册过(哪怕注册语句还在后面 JS 里,只要 HTML 中已存在该标签,且浏览器解析时识别为“待定义”) - CSS 必须在
内或之后加载(否则样式表注册时 DOM 还没解析出那个标签,选择器压根没机会匹配)
:defined 和 :not(:defined) 必须配 display 才能控尺寸
浏览器对未定义的自定义元素默认按 display: inline 渲染,哪怕你写了 width: 200px 或 background: red,也大概率不生效 —— 因为 inline 元素无视宽高、垂直边距、背景高度等。
正确写法示例:
my-card:not(:defined) {
display: inline-block; /* 或 block / flex */
width: 300px;
height: 200px;
background: #f5f5f5;
border: 1px dashed #ccc;
}
<p>my-card:defined {
display: block; /<em> 避免回退到 inline </em>/
}</p>
别用 :state(),它根本不存在
截至 2026 年 5 月,:state() 仍是 CSSWG 草案,Chrome/Firefox/Safari 全都不支持。你在代码里写 my-toggle:state(open),DevTools 会直接忽略这条规则。
真正可靠的状态响应方案只有这一种:
- 组件内部状态变更时,调用
this.setAttribute('data-state', 'loading')或this.toggleAttribute('data-state', true) - CSS 用属性选择器:
my-toggle[data-state="loading"] { opacity: 0.6; } - 避免用 class,因为 class 易被外部覆盖;
data-属性语义专一、可控性强
最常被忽略的一点::defined 的触发时机早于 connectedCallback,甚至早于 shadow DOM 挂载 —— 它只认 define() 这一行 JS 是否执行过。如果你依赖组件内部结构(比如某个 slot 是否有内容、某个子元素是否 ready),仅靠 :defined 无法判断,必须配合 JS 生命周期钩子。











