:first-child选中父元素的第一个直接子节点且类型匹配,而:first-of-type选中某类元素中的第一个;前者受注释、空格等文本节点影响,后者按类型分组取首,更符合日常语义。

first-child 选中的是“第一个子元素”,不是“第一个某类子元素”
很多人以为 :first-child 能挑出第一个 div 或第一个 p,其实它只看位置:只要某个元素是其父容器的**第一个直接子节点**,且类型匹配,才生效。如果父元素第一个子节点是 h1,后面才是 p,那 p:first-child 根本不会命中。
- 常见错误现象:
p:first-child在开发工具里没生效,检查 DOM 发现p前面有注释、空格文本节点,甚至隐藏的span - 使用场景:给列表第一项加顶部边框、重置首段外边距、避免标题前多一行空白
- 注意兼容性:IE9+ 支持,无需前缀;但 IE8 及以下只支持
:first-child(不支持:last-child等)
first-of-type 才是“第一个某类元素”的正确选择
如果你要选中父容器下第一个 div,不管它前面有没有 h2 或文本节点,得用 :first-of-type。它按元素类型分组后取首个,语义更贴近日常理解。
- 参数差异:
div:first-child→ 要求该div必须是父元素第一个子节点;div:first-of-type→ 只要求它是所有div中的第一个 - 性能影响:两者都是 CSS 伪类,浏览器渲染时计算开销极小,无需担心
- 简短示例:
<article> <h2>标题</h2> <p>第一段</p> <p>第二段</p><div class="aritcle_card flexRow"> <div class="artcardd flexRow"> <a class="aritcle_card_img" href="/ai/1547" title="Favird No-Code Tools"><img src="https://img.php.cn/upload/ai_manual/000/969/633/68b7a0e82b449361.png" alt="Favird No-Code Tools" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a href="/ai/1547" title="Favird No-Code Tools">Favird No-Code Tools</a> <p>无代码工具的聚合器</p> </div> <a href="/ai/1547" title="Favird No-Code Tools" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div><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> </article>此时p:first-child不匹配任何p,但p:first-of-type会匹配“第一段”
初始化样式时别盲目用 :first-child 清 margin/padding
用 :first-child 重置首元素外边距看似方便,但容易漏掉真实首元素类型,或被动态插入内容破坏结构。
- 常见错误现象:JS 插入一个提示
div到容器开头,原来生效的p:first-child突然失效,首段又冒出多余空白 - 更稳的做法:给容器设
margin-top: 0,再统一给所有子元素设margin-top: 1em,用通用性换健壮性 - 如果必须用伪类,优先考虑
:first-of-type,或配合类名如.content > *:first-of-type
HTML 注释和空格文本节点会影响 :first-child 匹配
CSS 的 :first-child 对 DOM 结构非常敏感——哪怕一个看不见的 HTML 注释 <!-- intro --> 或换行缩进产生的空白文本节点,都会让后续元素失去“第一个”身份。
- 验证方法:在浏览器开发者工具中展开父元素,看第一个子节点是不是你预期的元素;如果不是,点开那个文本节点或注释看看
- 规避方式:服务端模板或构建工具里压缩 HTML(移除冗余空白),或改用
:first-of-type - 注意:JS 动态创建元素时,
element.appendChild()不会产生文本节点,但innerHTML = '...'会按原始字符串解析,可能带空格
事情说清了就结束。真正麻烦的不是语法记不住,而是 DOM 结构比你写的 HTML 看起来复杂得多。









