:empty 无法匹配带空白字符的卡片,因其仅匹配无任何子节点(含文本节点)的元素;模板缩进、换行等空白字符会导致其失效。

为什么 :empty 无法匹配带空白字符的卡片?
直接用 :empty 判断卡片是否“空”,大概率会失效——它只认完全无子节点(包括文本节点)的元素。只要卡片里有空格、换行或 ,:empty 就返回 false。很多模板渲染后自带缩进换行,导致骨架屏死活不显示。
实操建议:
- 检查 DOM:打开开发者工具,右键卡片元素 → “Edit as HTML”,看内部是否残留空白文本节点
- 服务端/模板层主动 trim:比如 Vue 的
v-html前做str.trim(),或用v-if="content?.trim()"控制渲染 - 用
:has(*)+:not(:has(*))替代(Chrome 105+ / Safari 15.4+ 支持),它只关心是否有子元素,忽略纯文本
骨架屏样式该挂在哪一层?
别把骨架屏写在卡片容器上,否则 :empty 生效时整个卡片消失,用户看不到任何占位。正确做法是让骨架屏作为卡片的子元素,默认显示,仅在有真实内容时隐藏。
实操建议:
- 结构保持:
<div class="card"> <div class="skeleton"></div> <div class="content">...</div> </div>
- 用
.card:empty .skeleton { display: block; } 不行——<code>:empty不支持后代选择器穿透 - 改用:
.card:empty::before { content: ""; display: block; background: linear-gradient(...); ... },但仅限单层占位 - 更稳妥:用 JS 监听内容变化,或用
[data-empty]属性配合 CSS,由业务逻辑控制
:empty 和 display: none 冲突怎么办?
如果卡片内容被 JS 设置了 display: none(比如折叠面板),:empty 依然为 true——它只看 DOM 结构,不看渲染状态。这时候骨架屏会错误显示。
实操建议:
- 避免用
display: none隐藏内容来“模拟空状态”,改用移除节点或清空 innerHTML - 若必须保留 DOM,加个标记类:
.card.is-hidden .skeleton { display: block; },和:empty规则并存 - 注意优先级:CSS 中
.card:empty .skeleton无法生效,但.card:empty > .skeleton可以(需子选择符且骨架屏是直接子元素)
真正可靠的 fallback 方案长什么样?
纯 CSS 的 :empty 在真实项目中太脆弱。建议组合使用:
实操建议:
- 服务端/SSR 渲染时,直接输出
<div class="card" data-has-content="false">,CSS 写 <code>.card[data-has-content="false"] .skeleton { display: block; } - 客户端 hydration 后,用
MutationObserver监听.content的childList变化,动态切data-has-content - 骨架屏本身用
visibility: hidden而非display: none,避免重排;用opacity: 0.6+animation模拟加载感
最常被忽略的是:骨架屏的宽高必须和真实内容区域一致,否则布局会跳动。别依赖 height: auto,给 .skeleton 设固定 aspect-ratio 或 min-height。











