主题
第 02 章 · CSS 面试题集
Q1:CSS 盒模型有哪两种?区别是什么?
考察点:盒模型、box-sizing
标准答案: CSS 盒模型分两种:
- content-box(W3C 标准盒模型,默认):
width / height只算 content;元素实际占位 =width + padding + border。 - border-box(IE 盒模型 / 怪异盒模型):
width / height包含 content + padding + border;元素实际占位 =width。
通过 box-sizing 切换,现代项目几乎都用 border-box:
css
*, *::before, *::after { box-sizing: border-box; }通俗理解:
content-box:你说"我要 200 平米的房子",结果墙体厚度另算,实际占地 220 平。border-box:你说"占地 200 平",墙、保温层都包在 200 平里——所见即所得。
代码示例:
css
.a { width: 200px; padding: 20px; border: 5px solid; } /* 实际宽 250px */
.b { width: 200px; padding: 20px; border: 5px solid;
box-sizing: border-box; } /* 实际宽 200px */加分回答:
- 早期 IE6 用的就是
border-box,后来"反水"跟 W3C 走了content-box,现代项目又集体回到border-box——历史轮回。 - 第三方组件库经常带自己的
box-sizing,引入时检查冲突。 outline不计入任何盒模型尺寸,所以画聚焦圈用 outline 不会撑大布局。
追问:
- margin 是盒模型的一部分吗?(是,最外层)
- 行内元素的盒模型有什么不同?(margin/padding 上下不影响布局)
Q2:CSS 选择器优先级(权重)怎么算?
考察点:层叠、优先级
标准答案: 权重用 4 元组表示 (a, b, c, d),从高到低:
| 位 | 权重来源 |
|---|---|
| a | 行内 style="..." |
| b | id 选择器(#xxx)数量 |
| c | class、属性、伪类数量 |
| d | 标签、伪元素数量 |
比较时逐位对比,左边大的就赢。!important 高于一切(除了同样 important 时按上面规则比)。通配符 * 和组合符 > + ~ 不计权重。
通俗理解: 就像比职位高低——总裁(a)→部长(b)→经理(c)→员工(d)。10 个员工也比不过 1 个经理。
代码示例:
css
#nav .item a:hover { color: red; } /* (0,1,2,1) */
.menu .list .link { color: blue; } /* (0,0,3,0) */
/* 红色赢,因为有一个 id */加分回答:
:not(.x)本身不计权重,但括号里的选择器计权重。:where(...)权重永远是 0,常用于 reset。:is(.a, #b)取括号内最高权重那个。- 同权重时,后写的覆盖先写的。
- 避免 id 和
!important,多用 class,方便后期重写。
追问:
!important之间冲突怎么办?(按权重 + 顺序)- 行内 style 能被覆盖吗?(用
!important可以)
Q3:水平垂直居中有哪几种实现方式?
考察点:布局功底
标准答案: 按推荐度排序:
css
/* 1. Flex(最推荐) */
.parent { display: flex; justify-content: center; align-items: center; }
/* 2. Grid(更短) */
.parent { display: grid; place-items: center; }
/* 3. 绝对定位 + transform(无需知道宽高) */
.parent { position: relative; }
.child { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
/* 4. 绝对定位 + margin: auto(需要 inset 全 0) */
.child { position: absolute; top: 0; right: 0; bottom: 0; left: 0; margin: auto; }
/* 5. 行内元素居中:text-align + line-height */
.parent { text-align: center; line-height: 200px; } /* 父高度等于 line-height */通俗理解: 往一个空盒子的正中间贴一张贴纸——Flex 是"自动定位贴纸机",transform 是"先贴左上 50% 再回退自身一半"。
代码示例:
html
<div class="parent" style="display:flex;justify-content:center;align-items:center;height:200px;background:#eee">
<div style="background:#2563eb;color:#fff;padding:8px 16px">居中元素</div>
</div>加分回答:
- Flex 方案最简单且兼容好;Grid 的
place-items是align-items+justify-items的简写。 - transform 方案适合不知道子元素宽高的场景(如动态内容、文本行数不定)。
- 老项目还能见到
display: table-cell + vertical-align: middle。 - 文本垂直居中可以用
line-height等于容器高度,但只适合单行文本。
追问:
align-items和align-content区别?- 为什么
vertical-align: middle不能让 div 垂直居中?(仅对 inline / table-cell 生效)
Q4:position 的几种值有什么区别?
考察点:定位机制
标准答案:
| 值 | 脱离文档流 | 参照物 | 典型用途 |
|---|---|---|---|
static | 否 | — | 默认值 |
relative | 否 | 自己原位置 | 微调 / 给子绝对定位当地基 |
absolute | 是 | 最近的非 static 祖先 | 弹窗、tooltip、角标 |
fixed | 是 | 浏览器视口 | 回到顶部按钮、导航栏 |
sticky | 否(特殊) | 最近的滚动祖先 | 表格表头、章节标题吸顶 |
通俗理解:
relative:站在原位喊"我可以挪 5 步"——别人还是按你原来位置排队。absolute:飞起来贴在墙上——别人当你不存在。fixed:贴在车窗上——车开它不动。sticky:滚着滚着粘住——之后跟着滚条不动。
代码示例:
css
.tooltip-wrapper { position: relative; }
.tooltip {
position: absolute;
top: 100%; left: 0;
background: #333; color: #fff; padding: 4px 8px;
}
.back-to-top {
position: fixed;
bottom: 20px; right: 20px;
}
.section-title {
position: sticky; top: 0;
background: #fff;
}加分回答:
absolute/fixed元素如果没设 width,会收缩到内容宽度。sticky父元素必须有滚动且不能overflow: hidden,否则失效。position改变会创建新的 包含块和层叠上下文。- 设了
transform/filter的元素会成为其后代fixed的"伪 viewport"——这是常见 bug。
追问:
- z-index 在哪些 position 下生效?(除 static 外)
- 层叠上下文(stacking context)是什么?
Q5:Flex 布局的核心属性都有哪些?
考察点:Flex 布局
标准答案:
容器属性(写在父元素上):
display: flex / inline-flex— 启用flex-direction: row / column— 主轴方向flex-wrap: nowrap / wrap— 是否换行justify-content— 主轴对齐:flex-start / center / flex-end / space-between / space-around / space-evenlyalign-items— 交叉轴对齐:stretch / center / flex-start / flex-end / baselinealign-content— 多行的交叉轴对齐gap— 子项间距
子项属性:
flex-grow— 剩余空间分配比例(默认 0)flex-shrink— 空间不够时缩小比例(默认 1)flex-basis— 初始大小flex— 简写,常用flex: 1align-self— 单独覆盖父级align-itemsorder— 排序
通俗理解: Flex 就是让父元素当"队长",子元素自动排队。父元素发命令"横排还是竖排","间距怎么留","宽度不够要不要缩"。
代码示例:
css
.nav {
display: flex;
justify-content: space-between; /* 两端对齐 */
align-items: center; /* 垂直居中 */
}
.layout {
display: flex;
gap: 16px;
}
.layout > .side { width: 200px; }
.layout > .main { flex: 1; } /* 占满剩余 */加分回答:
flex: 1≡flex: 1 1 0,从 0 开始等比生长,所有子项等宽。flex: auto≡flex: 1 1 auto,从内容宽度开始生长,内容多的更宽。flex: none≡flex: 0 0 auto,完全不伸缩。- 主轴方向决定
justify-content作用方向,flex-direction: column时它管"上下"。 - IE10/11 的 flex 实现有 bug,老项目要小心。
追问:
- Flex 与 Grid 区别?
align-items和align-content区别?
Q6:Grid 和 Flex 有什么区别?什么时候用哪个?
考察点:布局方案选型
标准答案:
| 维度 | Flex | Grid |
|---|---|---|
| 维度 | 一维(行 或 列) | 二维(行 + 列) |
| 思维 | 内容驱动(先有子元素再排布) | 容器驱动(先画格子再放) |
| 适用 | 导航栏、按钮组、单行/单列对齐 | 整页布局、卡片网格、复杂表单 |
| 居中难度 | justify-content + align-items | place-items: center |
| 浏览器支持 | 极好(IE10+) | 现代浏览器(IE11 部分支持) |
经验法则:
- 整体布局用 Grid(页面分区、卡片墙)。
- 组件内部用 Flex(按钮内的图标+文字、卡片内部排列)。
- 两者可以嵌套:grid 容器里的某个格子又是 flex 容器。
通俗理解:
- Flex 像排队——一队人按顺序站成一行 / 一列。
- Grid 像棋盘——格子先画好,棋子各自往格子里放。
代码示例:
css
/* Grid 整页布局 */
.page {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-rows: 60px 1fr 40px;
grid-template-areas:
"header header"
"side main"
"footer footer";
}
/* 某个格子内部用 Flex */
.page > header {
grid-area: header;
display: flex;
justify-content: space-between;
align-items: center;
}加分回答:
- Grid 的
repeat(auto-fill, minmax(200px, 1fr))一行实现"自适应卡片墙",是 Flex 做不到的。 - Grid 的
gap早于 Flex 的gap(Flex 的 gap 是后期加的,Safari 14.1 才支持)。 - Grid 支持显式区域命名(
grid-template-areas),可读性极强。
追问:
grid-auto-flow是干嘛的?fr单位是什么?
Q7:CSS 中 em 和 rem 区别?
考察点:相对单位
标准答案:
| 单位 | 参照 | 累积效应 |
|---|---|---|
em | 当前元素的 font-size(继承自父级) | 嵌套时会层层累积 |
rem | 根元素 html 的 font-size | 全局统一,不累积 |
通俗理解:
em像"按当前部门标准"——你部门规定 14px,你的 1em 就是 14px;你下级部门加倍,下下级再加倍——传染病一样累积。rem像"按公司总部标准"——不管嵌套多深,都参照公司总部那一个数字,调一处全局变。
代码示例:
css
html { font-size: 16px; }
/* em 累积示例 */
.a { font-size: 1.5em; } /* 16 × 1.5 = 24px */
.a .b { font-size: 1.5em; } /* 24 × 1.5 = 36px ⚠️ */
/* rem 不累积 */
.x { font-size: 1.5rem; } /* 24px */
.x .y { font-size: 1.5rem; } /* 还是 24px */加分回答:
- 设字号大小用
rem,方便用户在浏览器调整字号(无障碍)。 - 设 padding/margin 等"和字号相关的间距"用
em(按钮内边距随按钮字号变化)。 - 移动端"全站缩放"套路:JS 监听 resize,动态改
html { font-size },全站 rem 自动跟随。 - 不要用 px 写正文字号——用户在系统里调大字号会失效。
追问:
vwvh是什么?100vh在手机上为什么会有问题?(地址栏遮挡,新提案dvh / svh / lvh)
Q8:CSS 动画 transition 和 animation 区别?
考察点:动画 API
标准答案:
| 维度 | transition | animation |
|---|---|---|
| 触发 | 状态变化(hover、class 切换) | 自动执行 |
| 关键帧 | 只有起止两点 | 可定义多个百分比节点 |
| 循环 | 不能循环 | 可 infinite 无限循环 |
| 复杂度 | 简单 | 复杂 |
| 暂停 | 不可 | animation-play-state: paused |
通俗理解:
- transition 像自动门:人靠近 → 开;人走远 → 关。只有"开"和"关"两态。
- animation 像预先编排好的舞蹈:从第 1 拍 → 第 2 拍 → ... → 第 8 拍,可以反复跳。
代码示例:
css
/* transition:hover 时背景变色,抬升 */
.btn {
background: #2563eb;
transition: background 0.3s, transform 0.2s;
}
.btn:hover {
background: #1d4ed8;
transform: translateY(-2px);
}
/* animation:心跳效果,无限循环 */
@keyframes heartbeat {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.1); }
}
.heart { animation: heartbeat 1s infinite; }加分回答:
- transition 必须有状态变化才会触发;
display: none到block是无法过渡的(值不连续)。 - 现代用
@starting-style可以让display: none → block动起来。 - 优先用
transform和opacity做动画——它们走 GPU 合成,60fps 不卡;width / left / top会触发重排,卡顿。 will-change: transform提示浏览器提前准备 GPU 层(不要滥用,会占内存)。prefers-reduced-motion媒体查询可以为有前庭障碍的用户关掉动画(无障碍)。
追问:
transition: all有什么问题?(无差别监听所有属性,性能差)- 为什么
transform比top性能好?
Q9:什么是 BFC?怎么触发?
考察点:CSS 渲染机制
标准答案: BFC(Block Formatting Context,块级格式化上下文)是 CSS 的一种独立渲染区域,内部元素的布局不会影响外部,外部也不影响内部。
触发方式:
float不为 noneoverflow不为 visible(hidden / auto / scroll)display: inline-block / flex / grid / flow-rootposition: absolute / fixed
作用(解决问题):
- 清除浮动:包含浮动子元素,撑开父容器高度。
- 避免 margin 折叠:BFC 内的元素和外部不会发生上下 margin 合并。
- 阻止文字环绕浮动元素。
通俗理解: BFC 像一个独立隔间——里面发生的事情(margin 折叠、浮动溢出),不会影响隔间外的人;反之亦然。
代码示例:
css
/* 父元素包含浮动子元素,需要撑开高度 */
.parent {
overflow: hidden; /* 触发 BFC,包含浮动 */
/* 或 display: flow-root(更现代、副作用最小) */
}
.child { float: left; width: 100px; height: 100px; }加分回答:
display: flow-root是 BFC 的"专用"触发方式,没有overflow: hidden的副作用(如裁剪阴影)。- BFC 内部的盒子按文档流垂直堆叠。
- 实际项目里,flex / grid 已经是 BFC,所以"清除浮动"问题大幅减少。
- 还有 IFC(行内格式化上下文)、GFC(grid)、FFC(flex)等概念。
追问:
- 有了 flex 和 grid 还需要 float 吗?(基本不用了)
- BFC、IFC、FFC 区别?
Q10:响应式设计有哪些常用手段?
考察点:响应式 / 自适应
标准答案:
viewport meta:移动端必备。
html<meta name="viewport" content="width=device-width, initial-scale=1.0" />媒体查询
@media:按屏宽切换样式。css@media (min-width: 768px) { /* 平板及以上 */ }流式布局:用
%、fr、flex让宽度弹性。css.container { width: 90%; max-width: 1200px; }rem / vw 字号缩放:
csshtml { font-size: clamp(14px, 2vw, 18px); }响应式图片:
<picture>+srcset+sizes。html<img srcset="small.jpg 480w, big.jpg 1200w" sizes="(min-width: 768px) 1200px, 100vw" />CSS Grid 自适应:
cssgrid-template-columns: repeat(auto-fill, minmax(200px, 1fr));clamp()函数:一行实现"最小-理想-最大"。cssfont-size: clamp(14px, 4vw, 24px);容器查询(新):根据父容器宽度而非视口。
css@container (min-width: 400px) { .card { display: flex; } }
通俗理解: 响应式 = 一份代码,让网页在不同屏幕上自己变形——就像变形金刚。
代码示例:
css
/* 移动优先 */
.container { padding: 12px; }
.card-grid { display: grid; grid-template-columns: 1fr; gap: 12px; }
@media (min-width: 768px) {
.card-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
.card-grid { grid-template-columns: repeat(3, 1fr); }
}加分回答:
- "响应式"和"自适应"严格说不一样:响应式是连续变化(流式),自适应是几个固定断点切换。
- 移动优先(mobile-first)比桌面优先更推荐:用
min-width渐进增强。 - 别只测断点边缘——测中间宽度(770px)和极端宽度(320px、1920px)。
prefers-color-scheme实现暗黑模式响应。
追问:
vw在 iframe 里是什么?- 如何处理高分屏(Retina)图片?