Skip to content

CSS 常见踩坑

CSS 开发中高频遇到的布局、兼容性和表现异常问题。


预防措施

  1. 优先使用现代布局:Flex 和 Grid 布局可避免大部分传统布局问题(如浮动塌陷、margin 重叠)
  2. 统一 z-index 管理:建立项目的 z-index 规范(如弹窗 1000、导航 900、内容 100),避免层级混乱
  3. 使用 CSS 预处理器变量:将颜色、间距等抽为变量,减少硬编码
  4. 真机测试 Retina 屏:1px 边框问题只在高分屏出现,务必真机验证
  5. 避免深层嵌套选择器:选择器层级不超过 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 替代

参考

个人学习笔记,部分内容借助 AI 辅助整理,仅供查阅参考,请以官方文档为准