zhangdizhangdi

CSS 面试点总结

简介

Cascading Style Sheets,层叠样式表

常见面试题

基础核心
  • 盒模型
  • margin 重叠是什么?如何解决?
  • BFC?如何触发?能解决什么问题?
  • display 的常见值
  • position 的常见值
  • z-index 层叠上下文
选择器
  • 常见选择器
  • 伪类、伪元素
  • 优先级(权重)
  • 浏览器选择器匹配原理
  • 为什么不建议用过深的选择器
布局
  • float
  • flex
  • grid
响应式
  • 媒体查询(@media)
  • px、em、rem、vw、vh、% 的区别?
  • 移动端适配方案有哪些?
动画与过渡
  • CSS 动画(animation)与 transition 的区别?
  • GPU加速 涉及哪些 CSS 属性(transform、opacity 等)?
  • will-change 是做什么的?为什么慎用?
工程化
  • 模块化
  • 预处理器
  • 样式隔离
  • Tailwind CSS 的核心思想是什么?为什么它越来越流行?
渲染性能
  • 浏览器的渲染流程
  • 为什么说 CSS 是阻塞渲染的?
  • Reflow(回流/重排)和 Repaint(重绘)的区别?
  • 如何处理 CSS 优化
实践应用
  • 垂直居中和水平居中
  • 两栏布局、三栏布局
  • 如何实现一个 1px 边框 的问题(尤其在 Retina 屏下)
  • 如何实现文字超出显示省略号(单行、多行)?
  • 实现一个三角形 / 扇形 / 梯形等图形的 CSS 原理?
  • 如何实现自适应正方形/圆形块?
其他
  • 哪些 CSS 属性可以继承?
  • line-height 如何继承?为何常用无单位写法?
  • 为什么 vertical-align: middle 经常不起作用?
  • display: none、visibility: hidden 和 opacity: 0 的区别?

盒模型

一个盒子由四个部分组成:content padding border margin

box-sizing 用来控制元素的盒子模型的解析模式,默认为 content-box

context-box:标准盒模型,默认
设置的height/width = content
盒子的宽/高 = width/height + padding + border

border-box:替代盒模型
设置的height/width = content + padding + border
盒子的宽/高 = width/height

⚠️ margin 不属于元素本身的宽高,但在计算元素占用的空间时会包含 margin。

参考

外边距重叠

垂直方向上,两个相邻块级元素的 margin 会合并成一个新的 margin 值。

计算规则
  • 正正取大
    • 上边 margin-bottom: 20px,下边 margin-top: 30px
    • 间距为 30px
  • 一正一负相加
    • 上边 margin-bottom: -20px,下边 margin-top: 30px
    • 间距为 10px
  • 负负取绝对值大(即取更小的那个)
    • 上边 margin-bottom: -20px,下边 margin-top: -30px
    • 间距为 -30px
常见场景
  • 相邻兄弟元素之间的 margin 会重叠
  • 父元素与第一个/最后一个子元素之间 margin 会重叠
  • 空元素自身的上下 margin 会互相重叠
html
<div class="a">ddd</div>
<!-- 30px -->
<div class="b">ddd</div>
<style>
  .a {
    margin-bottom: 30px;
  }
  .b {
    margin-top: 20px;
  }
</style>

<!-- 30px -->
<div class="parent">
  <div class="child"></div>
</div>
<style>
  .parent {
    margin-top: 20px;
  }
  .child {
    margin-top: 30px;
  }
</style>

<div>ddd</div>
<div class="empty"><!-- 30px --></div>
<div>ddd</div>
<style>
  .empty {
    margin-top: 30px;
    margin-bottom: 20px;
  }
</style>
解决方法

父子重叠:

  • 触发父元素 BFC
    • display: flow-root;
    • overflow: hidden;
  • 给父元素加 padding或border
  • 使用伪元素

兄弟重叠:

  • 调整数值
  • 其中一个包裹在触发 BFC 的容器中
参考

BFC

Block Formatting Context,块级格式化上下文。
它是一个独立的渲染区域,内部元素和外部元素互不影响。

创建BFC:
  • html
  • float: left、right
  • position: absolute、fixed
  • overflow: hidden、scroll、auto
  • display
    • flex/inline-flex
    • grid/inline-grid
    • inline-block
    • table、table-*、inline-table
    • flow-root
  • column-count、column-width、column-span
  • contain: size、layout、paint

🆕 flow-root:这是 CSS3 新增的属性,专门用来触发 BFC,且没有副作用(不会像 overflow: hidden 那样可能裁切内容)。

特性:
  • BFC 内部的盒子垂直方向从上到下排列
  • 同一个 BFC 内部的相邻元素会发生 margin 合并
    • BFC 之间不塌陷,这就是 BFC 能阻止 margin-collapse 的原因。
  • 浮动元素(float)不会覆盖 BFC 的布局区域
  • BFC 会包含内部浮动元素(高度不塌陷)
作用:

① 避免外边距(margin)重叠
父子、兄弟元素的 margin 本该塌陷,通过触发 BFC 可以阻止。

html
<div class="box">box</div>
<!-- 40px -->
<div class="container" style="overflow: hidden;">
  <div class="box">box</div>
</div>
<!-- 40px -->
<div class="box">box</div>
<!-- 20px -->
<div class="box">box</div>
<style>
  .box {
    margin: 20px 0;
  }
</style>

② 解决高度塌陷(清除浮动)
浮动元素不占高度,导致父容器高度塌陷;
触发 BFC 的容器可以包裹浮动元素,恢复高度。

html
<div class="parent">
  <div class="child"></div>
</div>
<style>
  .parent {
    /* 触发 BFC,解决高度塌陷 */
    overflow: hidden;
    /* 或者 */
    display: flow-root;

    background: gray;
  }
  .child {
    float: left;
    width: 100px;
    height: 100px;
  }
</style>

③ 阻止文本环绕(让浮动元素不影响旁边布局)
BFC 区块不会与 float 元素重叠,会出现在其旁边或下面。

html
<div class="float-box"></div>
<div class="text">
  文本不会再环绕浮动元素,因为 text 这个盒子形成了新的 BFC。 新的 BFC 会避开
  float 形成的影响,而是整体避让浮动元素。 因此这段文本会被“挤到下方”。
</div>
<style>
  .float-box {
    float: left;
    width: 30px;
    height: 30px;
    margin-right: 12px;
    background: #4aa3ff;
  }
  .text {
    /* 创建 BFC */
    overflow: hidden; /* 或者 */
    display: flow-root;

    background: #f0f0f0;
  }
</style>
参考

display

参考

position

  • static:正常的文档流,
  • relative:相对定位,相对于其父级元素
  • absolute:绝对定位,脱离文档流,以最近的不是static的父级元素为参考
  • fixed:绝对定位,以浏览器窗口作为参考
  • sticky:粘性定位,relative和fixed的混合
参考

z-index

z-index 属性决定了元素在页面 Z 轴(垂直于屏幕方向)上的堆叠顺序。简单来说,它决定了哪个元素盖在哪个元素上面。

  • 每个上下文内部有自己的绘制顺序,外部上下文无法穿透进去与其子元素竞争绘制优先。
  • z-index 只在同一个层叠上下文(stacking context)内比较。
创建新的层叠上下文

常见的:

  1. 根元素 html
  2. position
    • 为 absolute 或 relative 且 z-index 不为 auto 的元素
    • position 为 fixed 或 sticky 的元素
  3. display: flex/grid 的子元素, 且 z-index 值不为 auto
  4. opacity < 1
  5. transform 不是 none

更多的CSS3 新属性的情况,参考:MDN - 层叠上下文

层叠顺序

z-index stacking order
图:z-index stacking order

  1. 背景和边框:层叠上下文所属元素的 background 和 border(最底层)
  2. 负值 z-index:z-index < 0 的子元素
  3. 块级盒子:普通文档流中的 block 块级元素(用于布局)
  4. 浮动盒子:float 元素(非 none)
  5. 行内盒子:普通文档流中的 inline/inline-block 盒子(文字内容)
  6. z-index: 0 或 auto:定位元素(position 非 static),但没有设置正数 z-index
  7. 正值 z-index:z-index > 0 的子元素

z-index 相同:在 HTML 代码中后出现的元素在上。

Within each stacking context, the following layers are painted in back-to-front order:

  1. the background and borders of the element forming the stacking context.
  2. the child stacking contexts with negative stack levels (most negative first).
  3. the in-flow, non-inline-level, non-positioned descendants.
  4. the non-positioned floats.
  5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
  6. the child stacking contexts with stack level 0 and the positioned descendants with stack level 0.
  7. the child stacking contexts with positive stack levels (least positive first).
为什么 z-index 不生效
  1. 没加定位:元素是 static(默认),z-index 对其无效。
  2. 拼爹失败:父元素创建了层叠上下文,且父元素的 z-index 很低。子元素再高也没用。
  3. CSS3 属性干扰:父元素设置了 opacity < 1 或 transform,导致父元素变成了层叠上下文,限制了子元素的层级提升。
参考

选择器

  • 通配符 *
  • id选择器 #box
  • 类(class)选择器 .wrapper
  • 标签选择器 h1 div
  • 关系/组件选择器
    • 后代选择器 ,box .title,儿子、孙子…
    • 子代选择器 .box > .title,儿子
    • 相邻兄弟选择器 h1 + p,后面的第一个兄弟
    • 通用兄弟选择器 h1 ~ p,后面所有的兄弟
  • 属性选择器
    • [attribute] 选择带有attribute属性的元素
    • [attribute=value] 完全匹配
    • [attribute~=value] p[class~="special"] 包含单词
    • [attribute*=value] div[class*="box-"] 包含子串
    • [attribute^=value] 以…开头
    • [attribute$=value] 以…结尾
    • [attribute|=value] div[lang|="zh"]
  • 伪类选择器 a:hover
  • 伪元素选择器 .box::after
  • 群组选择器 div, p

伪类

用于选择元素的特定状态或位置
伪类列表 - MDN

部分:

  • :hover
  • :last-child
  • :first-child
  • :nth-child(n)
  • :is(selector)
  • :where(selector)
  • :has(selector)
  • :not(selector)

伪元素

用于选中元素的特定部分,或者创建虚拟元素。
伪元素列表 - MDN

部分:

  • ::before
  • ::after
  • ::first-letter
  • ::first-line
  • ::selection
参考

优先级权重

🚩 !important > 内联 > 优先级计算值
🚩 优先级相同时,显示后面的样式

三个不同的值相加,百(ID)十(类)个(元素)—— 三位数的三个位数

  1. 100:id
  2. 10:类、属性、伪类
  3. 1:标签、伪元素
  4. 0:通配符 *、组合/关系符号 + > ~,权重为 0,不影响优先级。

特殊情况:

  • :is():取内部最高权重
  • :not():取内部最高权重
  • :has():取内部最高权重(非常强大)
  • :where():优先级永远是 0,不管里面写什么
选择器 ID 标签 优先级
h1 0 0 1 0-0-1
h1 + p::first-letter 0 0 3 0-0-3
li > a[href*=“en-US”] > .inline-warning 0 2 2 0-2-2
#identifier 1 0 0 1-0-0
button:not(#mainBtn, .cta) 1 0 1 1-0-1

比较规则:

  • 从左往右依次进行比较 ,较大者优先级更高
  • 如果相等,则继续往右移动一位进行比较
  • 如果所有位全部相等,则后面的会覆盖前面的

🚨 注意:低等级的选择器数量再多,也无法超越高等级。
例如 100 个 class 叠加 (0, 100, 0) 也比不上 1 个 ID (1, 0, 0)

浏览器解析

❓ 浏览器是如何解析 CSS 选择器的?
✅ 浏览器从右向左匹配选择器

例子:div .box span

  1. 浏览器先找到页面上所有的 span。
  2. 然后向上查找,看它的父级是不是 .box。
  3. 如果是,再向上查找是不是 div。

原因:从右向左(先找子元素)能更快地过滤掉不符合条件的路径,效率比从左向右(先找根元素再向下遍历)高。

不使用过深的选择器

  • 性能问题
    • ✔ 深选择器 ≠ 性能灾难
    • ❌ 但确实会增加每个元素的“祖先链检查成本”
  • 可维护性差
    • 太依赖 DOM 结构,只要 HTML 结构稍改,样式就可能失效。
    • 难以阅读(语义不清晰)
  • 权重难控制
参考

float

特性

  • 脱离标准文档流
  • 浮动的元素变成行内块,在一行显示

浮动带来的问题:

  • 父元素的高度无法被撑开
  • 影响其他元素

清除浮动:

  • clear
    • 增加同级空标签
    • 父元素伪元素 ::after
  • BFC
    • 父元素设置 overflow
    • 父元素设置 display: flow-root
参考

flex

响应式

工程化

动画

  • animation 是动画属性,它的实现不需要触发事件,设定好时间之后可以自己执行,且可以循环一个动画。
  • transition 是过度属性,强调过度,它的实现需要触发一个事件(比如鼠标移动上去,焦点,点击等)才执行动画。

多数显示器默认频率是60Hz,即1秒刷新60次,所以理论上动画最小间隔为1/60*1000ms = 16.7ms。

参考

属性继承机制

给父元素设置了某些样式属性,子元素会自动拥有这些属性,而不需要在子元素上重新写一遍。

💛 默认继承

主要与文字内容的显示有关。

  1. 字体
    • font(简写)
    • font-family(字体族)
    • font-weight(粗细)
    • font-size(大小)
    • font-style(斜体等)
    • font-variant(小型大写字母)
  2. 文本
    • color(文本颜色)
    • line-height(行高)⚠️ 继承但有特殊计算机制
    • text-align(文本对齐方式)
    • text-indent(首行缩进)
    • text-transform(大小写转换)
    • letter-spacing(字间距)
    • word-spacing(单词间距)
    • white-space(换行处理)
    • direction(文字方向(ltr/rtl))
  3. 列表
    • list-style
    • list-style-type(如 disc, circle, decimal)
    • list-style-image
    • list-style-position
  4. 表格
    • border-collapse(边框合并)
    • border-spacing(边框间距)
    • caption-side(标题位置)
    • empty-cells(显示空单元格)
  5. visibility:父元素 visibility: hidden,子元素也会隐藏。但如果给子元素单独设置 visibility: visible,子元素可以显示出来。
  6. cursor:鼠标样式
💛 默认不继承

主要与元素的尺寸、定位、背景有关。

  • 盒模型:width, height, margin, padding, border
  • 背景:background
  • 定位与布局:position, top/right/bottom/left, z-index, float, display, overflow
  • vertical-align
  • opacity

🎈 父元素设置了背景色,子元素看起来也有背景色。
子元素的 background-color 默认值是 transparent(透明),所以你透过子元素看到了父元素的背景

🎈 父元素 opacity: 0.5,子元素也会变半透明。
这其实不是属性继承,而是层叠上下文的影响。父元素作为一个整体变透明了,里面的子元素自然也跟着变了。你无法通过给子元素设置 opacity: 1 来让它变回不透明

💛 控制继承
  • inherit:某个本来不继承的属性(比如 border)也能从父元素那里继承

line-height?为何常用无单位写法?

为什么 vertical-align: middle 经常不起作用?

💡 属性只对行内级元素(inline、inline-block)和表格单元格(table-cell)生效。
💡 它让元素的“中线”与父行框的“中线”对齐,而不是让元素在父容器垂直居中。
💡 它不是用来垂直居中块元素的。

参考

display: none、visibility: hidden、opacity: 0 的区别

display: none visibility: hidden opacity: 0
可见性
占据空间
重排/回流 Reflow (回流) Repaint (重绘) 配合 GPU 加速(硬件加速)
点击/触发事件
子元素显隐 ✅子元素设为 visible 可见
过渡动画
场景推荐
  • 完全移除元素(如 Tab 切换未选中的面板):用 display: none。
  • 需要保留位置,但不想要响应事件(如加载占位符):用 visibility: hidden。
  • 制作淡入淡出动画,或者看起来隐藏但依然需要点击(如覆盖在图片上的透明点击层):用 opacity: 0。

收藏文章