:only-child要求父元素子节点总数为1(含文本、注释等),否则失效;:only-of-type仅统计同标签兄弟数量,忽略空白与异类节点;拼写错误如“:only-of-child”会导致规则静默失效。

:only-child 看的是 DOM 子节点总数,不是“有没有兄弟标签”
它只认一件事:element.parentNode.children.length === 1。只要父容器里子节点数不等于 1,哪怕只有一个 <p>,但旁边有换行、空格或注释,:only-child 就不生效——因为那些空白也是 Text 节点。
常见错误现象:p:only-child 写好了,样式没反应。打开 DevTools 查看 Elements 面板,展开父元素,大概率能看到前后各一个灰色的 #text 节点。
- HTML 写成
<div><p>ok</p></div>→p是:only-child - HTML 写成
<div>\n <p>ok</p>\n</div>→p不是:only-child(子节点数为 3) - 模板引擎(如 Vue SFC、PHP 拼接)自动插入空格/换行 →
:only-child极易意外失效
:only-of-type 数的是同类型兄弟,对空白和异类节点免疫
:only-of-type 的判断逻辑是:Array.from(parent.children).filter(el => el.tagName === element.tagName).length === 1。它完全忽略 Text、Comment 节点,也不管有没有 <span> 或 <div> 在旁边。
使用场景:卡片里固定要放一张图,但可能附带遮罩层 <div class="overlay"> 或图标 <svg> ——这时 img:only-of-type 依然能命中,而 img:only-child 会直接挂掉。
立即学习“前端免费学习笔记(深入)”;
-
<article><img src="a.jpg"><div class="meta">...</div></article>→img:only-of-type✅,img:only-child❌ -
<section><p>A</p><span>B</span></section>→p:only-of-type✅(唯一<p>),p:only-child❌(两个子节点) - SSR 渲染或 CMS 输出 HTML 时几乎必然含空白 → 优先选
:only-of-type更稳
别写错名字,p:only-of-child 根本不存在
这是笔试和 Code Review 里高频出错点。CSS 规范里只有 :only-child 和 :only-of-type,没有 :only-of-child 这种写法——浏览器直接忽略整条规则,且 DevTools 不报错,只会静默失效。
PigX UI Pro 前端开发指南 - Vue 3 + TypeScript + Element Plus。当用户提到 PigX UI、PigX 前端、lgb-mgui 项目、Vue 3 企业级后台开发、Element Plus 后台开发时使用此技能。
容易混淆的原因是命名结构相似,但逻辑完全不同:-of-type 是固定后缀,only-of-child 是把两个词硬拼起来的臆造语法。
- 正确写法:
li:only-child、button:only-of-type - 错误写法:
li:only-of-child、button:only-child-of-type - 验证方式:在 DevTools 的 Styles 面板里,如果某条规则灰了(被划掉),先检查是不是拼错了伪类名
真要靠数量做判断,JS 才是最终兜底方案
CSS 的伪类本质是声明式匹配,没法动态计算“是否仅含一个有效子元素”。当业务逻辑要求严格响应子节点数量(比如“仅有一个可交互元素才启用 hover 效果”),仅靠 CSS 容易翻车。
简单可靠的 JS 判断方式:
if (parent.children.length === 1 && parent.children[0].nodeType === 1) {
parent.classList.add('has-exactly-one-element');
}
注意这里加了 nodeType === 1 判断,排除 Text 和 Comment 节点,比单纯数 children.length 更贴近业务语义。
复杂点在于:这个判断需要在 DOM 更新后重新执行(比如 React/Vue 动态增删子节点),不能只跑一次。容易被忽略的是 MutationObserver 或框架的生命周期钩子介入时机 —— 晚了样式没更新,早了节点还没挂载。

















