CSS 常见踩坑
CSS 开发中高频遇到的布局、兼容性和表现异常问题。
预防措施
- 优先使用现代布局:Flex 和 Grid 布局可避免大部分传统布局问题(如浮动塌陷、margin 重叠)
- 统一 z-index 管理:建立项目的 z-index 规范(如弹窗 1000、导航 900、内容 100),避免层级混乱
- 使用 CSS 预处理器变量:将颜色、间距等抽为变量,减少硬编码
- 真机测试 Retina 屏:1px 边框问题只在高分屏出现,务必真机验证
- 避免深层嵌套选择器:选择器层级不超过 3 层,降低优先级冲突风险
1. margin 重叠(外边距合并)
css
/* 相邻块级元素的 margin 会合并为较大的那个 */
.box-a { margin-bottom: 20px; }
.box-b { margin-top: 30px; }
/* 实际间距是 30px,不是 50px */
/* 父子元素也会发生:子元素的 margin-top 会穿透到父元素外 */
.parent { background: #fff; }
.child { margin-top: 20px; } /* 看起来是 parent 有了 margin-top */css
/* 解决方案 */
.parent {
overflow: hidden; /* 创建 BFC(Block Formatting Context,块级格式化上下文) */
/* BFC 是一个独立的渲染区域,内部元素的布局不会影响外部,可阻止 margin 重叠 */
/* 或 */
padding-top: 1px; /* 用 padding 隔开,避免父子 margin 直接接触 */
/* 或 */
display: flex; /* Flex 布局不会 margin 重叠 */
}2. 浮动导致父元素高度塌陷
css
/* 子元素 float 后,父元素高度变为 0 */
.parent {
background: #f5f5f5;
}
.child {
float: left;
width: 200px;
height: 100px;
}
/* parent 高度为 0,背景色看不到 */css
/* 解决方案 */
.parent::after {
content: '';
display: block;
clear: both; /* 清除浮动 */
}
/* 或直接用现代布局替代浮动 */
.parent {
display: flex; /* ✅ 推荐 */
}3. z-index 不生效
css
/* z-index 只在定位元素上生效 */
.box {
z-index: 999; /* ❌ 没有 position,不生效 */
}
/* ✅ 必须配合定位 */
.box {
position: relative; /* 或 absolute / fixed / sticky */
z-index: 999;
}css
/* z-index 层叠上下文(Stacking Context)问题 */
/* 层叠上下文是浏览器决定元素堆叠顺序的机制,每个层叠上下文是一个独立的"层" */
/* 子元素的 z-index 只在同一层叠上下文内比较,无法突破父级上下文 */
.parent-a {
position: relative;
z-index: 1; /* 创建了层叠上下文,z-index 为 1 */
}
.parent-a .child {
z-index: 9999; /* 再高也出不了 parent-a 的层叠上下文 */
}
.parent-b {
position: relative;
z-index: 2; /* parent-b 的层叠上下文层级为 2,高于 parent-a 的 1 */
}
/* parent-b 的 z-index: 2 > parent-a 的 z-index: 1 */
/* 所以 parent-b 的所有子元素都在 parent-a 的所有子元素之上 */4. 1px 边框在 Retina 屏变粗
css
/* Retina 屏(视网膜屏幕)是苹果推出的高分辨率显示屏,像素密度是普通屏的 2 倍或 3 倍 */
/* 在 2x/3x 屏幕下,CSS 的 1px 实际占 2/3 个物理像素,导致边框看起来变粗 */
/* 方案一:伪元素 + transform 缩放 */
.border-1px {
position: relative; /* 为伪元素定位提供参考 */
}
.border-1px::after {
content: ''; /* 伪元素必须有 content */
position: absolute; /* 绝对定位 */
left: 0; /* 左对齐 */
bottom: 0; /* 底部对齐 */
width: 100%; /* 宽度撑满 */
height: 1px; /* 高度 1px */
background: #ddd; /* 边框颜色 */
transform: scaleY(0.5); /* 2x 屏幕缩放 0.5,使 1px 变为 0.5 物理像素 */
}
/* 方案二:直接用 0.5px(部分浏览器支持) */
.border {
border: 0.5px solid #ddd; /* iOS 8+ 支持,Android 部分支持 */
}5. Flex 布局不换行
css
/* 子元素宽度设了百分比但不换行 */
.parent {
display: flex;
}
.child {
width: 25%;
/* ❌ 默认 flex-shrink: 1,内容多时会被压缩 */
}css
/* 解决方案 */
.child {
flex: 0 0 25%; /* 不放大、不缩小、基础宽度 25% */
/* 或 */
min-width: 25%; /* 设置最小宽度 */
}6. overflow: hidden 不生效
css
/* 父元素设置了 overflow: hidden,子元素还是溢出 */
.parent {
overflow: hidden;
}
/* 原因:子元素使用了 position: absolute/fixed */
.child {
position: absolute;
top: 0;
left: 0;
width: 200px;
height: 200px;
}css
/* 解决方案:父元素也设置定位 */
.parent {
position: relative; /* 创建包含块 */
overflow: hidden;
}7. 图片底部有空隙
css
/* img 是 inline 元素,默认与基线对齐,底部留有间隙 */
.container img {
/* ❌ 图片底部有几像素空隙 */
}css
/* 解决方案 */
.container img {
display: block; /* ✅ 块级元素无基线对齐 */
/* 或 */
vertical-align: bottom; /* 底部对齐 */
/* 或 */
font-size: 0; /* 父元素字号设 0 */
}8. 文字溢出省略号不生效
css
/* 单行省略 */
.ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
/* ❌ 必须有固定宽度,否则不生效 */
width: 200px; /* ✅ 必须 */
}css
/* 多行省略 */
.ellipsis-2 {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
/* ❌ 父元素不能有固定高度 */
/* ❌ 不能用 flex 布局的子元素 */
}9. position: sticky 不生效
css
/* sticky 需要指定 top/bottom/left/right 中至少一个 */
.nav {
position: sticky;
/* ❌ 没有 top 值,不生效 */
}css
/* ✅ 正确用法 */
.nav {
position: sticky;
top: 0; /* 必须指定 */
}
/* ⚠️ 注意:父元素不能有 overflow: hidden/scroll/auto */
.parent {
/* overflow: hidden; ← 会导致 sticky 失效 */
}10. CSS 变量在媒体查询中不生效
css
/* ❌ 变量定义和使用分离时注意优先级 */
:root {
--size: 100px;
}
@media (max-width: 768px) {
:root {
--size: 50px; /* ✅ 重新定义变量 */
}
}
.box {
width: var(--size); /* 会根据屏幕宽度自动切换 */
}11. transform 导致 fixed 定位失效
css
/* 祖先元素有 transform 时,fixed 定位变成相对该祖先 */
.parent {
transform: translateX(0); /* 创建了新的包含块 */
}
.child {
position: fixed;
top: 0;
left: 0;
/* ❌ 相对于 .parent 定位,不是视口 */
}css
/* 解决方案:避免在 fixed 元素的祖先上使用 transform */
/* 或改用 position: absolute 配合视口高度 */12. 选择器优先级导致样式不生效
css
/* 内联 > ID > 类 > 元素 */
#app .card .title { color: red; } /* 1-0-0 + 0-1-0 + 0-1-0 = 1-2-0 */
.title { color: blue; } /* 0-1-0,优先级低,不生效 */
/* ❌ 用 !important 强制覆盖(难以维护) */
.title { color: blue !important; }
/* ✅ 提升选择器特异性 */
.card .title { color: blue; }真实场景
场景 1:弹窗被其他元素遮挡
- 问题:设置
z-index: 9999的弹窗仍然被页面内容遮挡 - 原因:弹窗的父元素创建了层叠上下文(如
position: relative; z-index: 1),子元素无法突破父级上下文 - 解决:将弹窗提升到与遮罩同级的 DOM 层级,或调整父元素的 z-index
场景 2:移动端底部导航栏 1px 边框变粗
- 问题:设计稿的 1px 细线边框在 iPhone 上看起来像 2px
- 原因:Retina 屏像素密度为 2x,CSS 的 1px 实际占 2 个物理像素
- 解决:使用伪元素 +
transform: scaleY(0.5)实现真正的 1 物理像素边框
场景 3:子元素 margin-top 穿透到父元素
- 问题:给子元素设置
margin-top: 20px,结果整个父元素被推下来了 - 原因:父子元素的 margin 发生了重叠(外边距合并)
- 解决:给父元素添加
overflow: hidden创建 BFC,或使用padding-top替代