用全会,问全废:CSS高频面试题

如何实现一个三角形?

原理:border

在前端 CSS 里,要把方块转变为三角形,主要借助边框属性来实现。下面为你介绍具体的实现方法:

css 复制代码
.triangle {
  width: 0;
  height: 0;
  border-left: 50px solid transparent;
  border-right: 50px solid transparent;
  border-bottom: 100px solid #3498db;
}

实现方法

原理:元素的border是由三角形组合而成

  • 宽高设为 0:把元素的宽度和高度都设成 0,这样元素本身就不会占据空间。
  • 利用边框:通过设置不同方向的边框,让它们相互叠加,从而形成三角形。
  • 控制边框颜色:将左右边框的颜色设为透明,底部边框设置成你想要的颜色,这样就呈现出一个朝上的三角形。

改变三角形的方向:

  • 朝上的三角形:左右边框透明,底部边框设置颜色。
  • 朝下的三角形:左右边框透明,顶部边框设置颜色。
  • 朝左的三角形:上下边框透明,右边框设置颜色。
  • 朝右的三角形:上下边框透明,左边框设置颜色。

下面是一个朝下三角形的示例:

css 复制代码
.triangle-down {
  width: 0;
  height: 0;
  border-left: 50px solid transparent;
  border-right: 50px solid transparent;
  border-top: 100px solid #e74c3c;
}

Flex:1 和 Flex auto 的区别?

在 Flexbox 布局中,flex: autoflex: 1 是两个不同的简写值,简写语法如下

ini 复制代码
flex: [flex-grow] [flex-shrink] [flex-basis];

flex-grow

定义如何瓜分主轴上的剩余空间,默认值是 0,即使还有剩余空间也不瓜分。

  • 数值越大,分配到的剩余空间越大(按比例来,如下图所示)
  • 计算方式:剩余空间 x,flex-grow 的值分别为 a,b,c
    • 每个元素可以分配的剩余空间为: a/(a+b+c) * x,b/(a+b+c) * x,c/(a+b+c) * x
    • 以A为例:1/(1+2+3) = 1/6,A"瓜分"到的 150*1/6=25,实际宽度为100+25=125

flex-shrink

由于 flex 默认不换行,所以当父容器空间不足时,则将子元素"收缩",默认值为 1(即空间不够时会缩小)

  • 数值越大,收缩比例越高(如下图)
  • 计算方式:三个元素的宽度为 w1 w2 w3,flex-shrink 分别为 a,b,c,以 A 元素为例计算
    • 计算总压缩权重: sum = a * w1 + b * w2 + c * w3(300 * 1 + 150 * 2 + 200 * 3 = 1200)
    • 计算每个元素压缩率: S1 = a * w1 / sum(300*1 / 1200 = 0.25)
    • 计算每个元素宽度:width - 压缩率 * 溢出空间(300 - (150 * 0.25) = 262.5)

注意:

  • 如果子容器没有超出父容器,设置 flex-shrink 无效
  • 计算方式和 flex-grow 是不一样的

flex-basis

指定了 flex 元素在主轴方向上的初始大小,默认值为 auto(即由项目自身内容或 width/height 决定),也可以设为具体数值(如 100px)或百分比。

  • 优先级:max-width/min-width > flex-basis > width > box
  • flex-basis 是「初始尺寸」,flex-grow(扩展)和 flex-shrink(收缩)都是基于这个初始尺寸来计算空间分配 / 收缩的

flex: 1 和 flex: auto 的区别

真正的差异只有 flex-basis:一个是 0%,一个是 auto。

简写属性 完整写法 flex-grow flex-shrink flex-basis
flex:1 flex: 1 1 0% 1 1 0%
flex:auto flex: 1 1 auto 1 1 auto

flex:1

  • 忽略自身元素内容大小,强制按比例均分空间
  • 如下所示,即使文本长度不一样,每个子元素的宽度依旧相等

flex: auto

  • 优先保留元素自身内容尺寸,再分剩余空间。
  • 如下图所示,文本长度(元素宽度)不一样时,每个元素宽度不一样

应用场景

需要 "等分空间"------ 用 flex:1

  • 如卡片等分显示,多个导航栏均等显示

需要"优先保证内容显示"------ 用 flex:auto

  • 搜索框布局,需要优先保证输入框可输入宽度

Float 为什么要清除浮动?以及如何清除?

最开始的作用:图文环绕

要实现这样的效果,决定了 float 的两个特性:

  • "破坏文档流",使得父元素得高度塌陷。
  • 禁止行框盒子与浮动元素发生重叠

一:为什么要清除浮动?

当子元素设置了float: left/right后,脱离文档流会引发以下问题:

  1. 父容器高度塌陷 :父元素的高度默认由子元素撑开,但浮动元素 "飘" 起来后,父元素会认为内部没有可撑开高度的内容,高度变为 0
  2. 布局混乱:页面下方的元素会 "钻" 到浮动元素下方,导致排版错位、重叠等问题。
  3. 背景和边框显示异常:父容器的背景和边框无法正常包裹浮动元素。

如以下布局:

ini 复制代码
<div class="parent">
  <div class="float-item">浮动元素</div>
</div>
<div class="next">后续元素</div>

二:如何清除浮动?(按推荐度排序)

以下是常见的清除浮动方法:

clearfix 伪元素法(最佳实践 / 主流方案)

通过 CSS 为父容器添加伪元素,自动清除浮动。

  • 原理:clear: both 表示元素的左右两侧都不允许有浮动元素;
  • 不增加冗余 HTML 标签,兼容性好(支持所有主流浏览器)
  • 可封装成通用类
css 复制代码
.clearfix::after {
  content: ""; 
  display: block; 
  clear: both; /* 核心:清除左右浮动 */
  visibility: hidden; /* 隐藏伪元素 */
  height: 0; /* 避免占用额外空间 */
}

BFC(块级格式化上下文)

为父容器设置overflow: auto/hidden,使其创建 BFC

  • 原理:BFC 会包裹内部的浮动元素,自动计算父元素高度。
  • 缺点:如果子元素有超出父元素的内容(如下拉菜单、弹出层),会被 overflow: hidden 裁剪掉,适合简单布局。

使用 flexbox 或 Grid 布局

  • 原理:现代布局方式会自动包含子元素,无需清除浮动。
  • 缺点:兼容性没上面的高

额外标签法(不推荐,冗余标签)

在所有浮动子元素的末尾,添加一个空的块级标签(如 <div>),并设置 clear: both

  • 原理:直接在浮动元素后插入一个清除浮动的元素,强制父元素识别高度;
  • 缺点:增加无意义的 HTML 冗余标签

如何实现 0.5px 边框?

在标准 css 中,通常无法直接设置 0.5px 的边框宽度,因为 css 像素的浏览器的最小渲染单位,而不支持亚像素级的渲染。但是可以通过一些技巧来模拟实现。

伪元素

可以使用伪元素 ::before 或 ::after 来创建一个额外的边框层,并设置高度为 0.5px。

css 复制代码
.element {
    position: relative;
}

.element::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    width: 200%; /* 缩放后还原宽度 */
    height: 200%; /* 缩放后还原高度 */
    border: 1px solid #ccc; /* 先设 1px 边框 */
    transform: scale(0.5); /* 缩放 0.5 倍,边框变为 0.5px */
    transform-origin: 0 0; /* 缩放原点定位到左上角,避免偏移 */
    box-sizing: border-box; /* 边框计入宽高,防止溢出 */
    pointer-events: none; /* 伪元素不拦截点击事件 */
}

阴影效果

可以使用 box-shadow 属性来模拟一个细边框的效果

css 复制代码
.element {
    border: 1px solid transparent; /* 隐藏主边框 */
    box-shadow: 0 0 0 0.5px #000; /* 模拟 0.5px 边框 */
}

em 与 rem 的区别?

1. em (相对单位)

  • 参考基准 :当前元素的字体大小font-size)。
  • 计算规则 :如果当前元素的 font-size16px,则 1em = 16px2em = 32px
  • 特点嵌套元素会继承父元素的字体大小,导致多次嵌套时可能出现尺寸累积。

2. rem (根相对单位)

  • 参考基准根元素 (即 <html> 标签)的字体大小。
  • 计算规则 :如果 <html>font-size16px,则 1rem = 16px2rem = 32px
  • 特点全局统一,不受父元素字体大小影响,避免嵌套层级过深导致的尺寸失控。

浏览器的默认字体尺寸为 16px,所以有以下设置

css 复制代码
html {
    font-size: 62.%; /* 公式16px*62.5%=10px */
}

什么是 BFC?

BFC (块级格式化上下文):

  • 简单来说,BFC是一个完全独立的空间(布局环境),让空间里的子元素不会影响到外面的布局

当浮动时,会导致高度塌陷问题,如下图所示:

  • 黄色框向左浮动,使它脱离了原本的位置,由此绿色框被定位到灰色父元素,并与浮动元素重叠
  • 由于父元素灰色框没有创建 BFC,由此计算高度时不会考虑灰色框的区域,也就是常见的 高度坍塌 问题
arduino 复制代码
<div class='box'> // 灰色框
    <div class='left' /> // 黄色框,向左浮动
    <div class='right' /> // 绿色框
</div>

如何创建 BFC

  • 根元素或其他包含它的元素
  • 浮动:float: 不为 none
  • 绝对定位元素:position: absolute / fixed
  • 行内块级元素:display: inline-block
  • 表格单元格:display: table-cell
  • overflow 的值 不为 visible 的元素
  • 弹性盒子:display: flex / inline-flex

BFC 的应用和效果

应用一:防止高度坍塌

给灰色框创建一个新的 BFC 后,高度计算时会包括浮动元素(灰色框高度变化)

ini 复制代码
.BFC {
    overflow: hiddle;
}

<div className='box BFC'>
    <div className='left' />
    <div className='right' />
</div>

应用二:阻止与浮动元素重叠

  • 如下例:由于绿色框没有创建 BFC,其中的白色块受到黄色框(浮动元素)的影响,被挤到右边的同时超出了绿色框的范围。
xml 复制代码
<style>
    .little{
        background: #fff;
        width: 50px;
        height: 50px;
        margin: 10px;
        float: left;
    }
</style>

<div className='box BFC'>
    <div className='left' />
    <div className='right'>
        <div className='little' />
        <div className='little' />
        <div className='little' />
    </div>
</div>
  • 给绿色框创建 BFC 后,则并不会与黄色框发生重叠,同时由于 BFC 的隔离作用(一个独立容器),白色块不会受到黄色浮动框的影响(也说明其 每个元素的margin box的左边,与容器块border box的左边相接触
  • 注意:触发 BFC 不能阻止其他形式的脱离文档流的元素覆盖正常流元素(比如绝对定位元素)
css 复制代码
<div className='right BFC'>
    ...
</div>

应用三:阻止 margin 合并

阻止相邻元素的 margin 合并

给两个框分别添加 margin,由图可知相邻的 margin 会被合并

ini 复制代码
<div className="box BFC">
    <div className="left" /> // margin: 20px;
    <div className="right" /> // margin: 20px;
</div>
  • 给其中一个元素(也可以两个元素)设置 BFC,可以阻止 margin 的合并
  • 注意:块级元素会默认铺满一行
ini 复制代码
<div className="box BFC">
    <div className="BFC">
        <div className="left" />
    </div>
    <div className="right" />
</div>

应用四:自适应两栏布局

  • 利用 BFC 不会与浮动元素重叠的特性,同时由于块级元素默认铺满整行,就可以实现两栏自适应布局
ini 复制代码
<div className="box BFC">
    <div className="left" /> // 保持左浮动
    <div className="right BFC" /> // 删除 width
</div>

什么是盒模型?

可以认为每个html标签都是一个方块,然后这个方块又包着几个小方块,如同盒子一层层的包裹着,这就是所谓的盒模型。

默认width只是内容宽度,不是整体大小。

W3C 标准盒模型:box-sizing: content-box

内容的宽高度不包含 border 和 padding

  • 属性 width, height 只包含内容 content ,不包含 border 和 padding。
  • width = 内容的宽度
  • height = 内容的高度

实际占用空间大于设定的宽高。

IE 盒模型:box-sizing: border-box

内容的宽高度包含 border 和 padding

  • 属性 width, height 包含 border 和 padding,指的是 content+padding+border。
  • width = border + padding + 内容的宽度
  • height = border + padding + 内容的高度

这种模式的优势在于:

  • 可以确切知道每个元素会占据多少空间,特别适合做栅格系统、卡片布局等需要精确控制尺寸的场景。

使用技巧

  • 如果不想让 padding 或 margin 改变盒模型定义的宽高,则使用 border-box
  • 尽量使用标准的W3C模型(需在页面中声明 DOCTYPE 类型),这样可以避免多个浏览器对同一页面的不兼容
    • 若不声明 DOCTYPE 类型,IE浏览器会将盒子模型解释为IE盒子模型,FireFox 等会将其解释为 W3C 盒子模型;
    • 若在页面中声明了 DOCTYPE 类型,所有的浏览器都会把盒模型解释为 W3C 盒模型。

css有哪些选择器?以及优先级?

层叠(Cascading)是CSS的一个核心机制,它决定了当多个样式规则应用到同一个元素时,哪些规则会生效,以及生效的顺序。

常见的 css 选择器

  • 元素选择器(Element Selector):选择指定元素类型的所有元素(p
  • 类选择器(Class Selector):选择具有指定类名的元素,使用"."符号表示(.class
  • ID选择器(ID Selector):选择具有指定id的唯一元素,使用"#"符号表示(#myId
  • 属性选择器(Attribute Selector):选择具有指定属性或属性值的元素([type="text"]
  • 后代选择器(Descendant Selector):选择指定元素的后代元素,使用空格分隔,例如div p选择所有div元素内部的p元素。
  • 直接子元素选择器(Child Selector):选择指定元素的直接子元素,使用">"符号表示,例如div > p选择所有div元素的直接子元素p。
  • 伪类选择器(Pseudo-class Selector):选择具有特定状态或行为的元素,例如:hover选择鼠标悬停的元素。
  • 伪元素选择器(Pseudo-element Selector):选择元素的特定部分,例如::before选择元素的前面插入内容。

选择器优先级和权重列表

样式类型 权重值 示例
内联样式 1000 style="color: red"
ID选择器 100 #header
类选择器 10 .blue, .active
元素选择器 1 div, p
通用选择器 0 *

注意点:

  • CSS 选择器的特殊性(Specificity,权重) 遵循位权计算规则 ,存在层级壁垒 ,低层级选择器的数量无论堆叠多少,都永远无法跨越层级超过高层级选择器,这是和普通「数字加法」最本质的区别。
  • 高位权重只要有值,低位权重堆到无穷大也无法超越。
  • 同权重时,「后声明」覆盖「先声明」
  • 继承的样式权重为 0, 会被子元素自身的任意选择器覆盖

重绘与回流的区别?

重绘(Repaint)和回流(Reflow)是与浏览器渲染引擎相关的概念,它们可以影响网页的性能。

回流

先画「骨架」------ 确定每个元素的位置、大小、间距(比如盒子的宽高、top/left、margin);

如果骨架变了(如盒子宽度变化),就需要重新计算所有相关元素的位置和尺寸,这个过程就是回流

常见操作:(只要改变元素的几何属性,就会触发回流)

  • 修改元素尺寸、位置、字体、文本
  • 增减 / 修改 DOM 节点
  • 修改浏览器窗口
  • 读取某些布局属性:offsetWidth/offsetHeight/clientWidth/getComputedStyle()(浏览器为了获取准确值,会强制触发回流)。
  • 使用 JavaScript 计算样式或布局。

重绘

骨架不变,只改「外观」------ 比如改颜色、背景、阴影、透明度。

只需要在原有骨架上重新上色,不用动结构,这个过程就是重绘

常见操作:只改视觉样式,不碰布局,仅触发重绘

  • 修改颜色、阴影、透明度、背景图、光标等

性能优化:减少不必要的回流/重绘

批量修改,减少多次回流

  1. 合并样式修改
ini 复制代码
const box = document.getElementById('box');

// 每改一个样式,触发一次回流
box.style.width = '200px';
box.style.height = '100px';

// 合并style属性 或 添加类名
box.style.cssText = 'width:200px; height:100px; margin:20px;';
// CSS:.box-new { width:200px; height:100px; margin:20px; }
box.classList.add('box-new');
  1. 先「离线」修改 DOM,再「上线」

把元素从 DOM 树中移除,修改完成后再加回去(仅触发 2 次回流:移除 + 添加)

ini 复制代码
const ul = document.getElementById('list');
// 1. 离线:移除DOM
ul.style.display = 'none';
// 2. 批量修改(无回流)
for (let i = 0; i < 100; i++) {
  const li = document.createElement('li');
  li.textContent = `Item ${i}`;
  ul.appendChild(li);
}
// 3. 上线:重新显示
ul.style.display = 'block';

避免频繁读取「触发回流的属性」

如下例所示:每次读 offsetWidth 都触发回流,共 100 次,如果先缓存属性值,则仅触发一次;

ini 复制代码
const box = document.getElementById('box');
let width = 0;
for (let i = 0; i < 100; i++) {
  width = box.offsetWidth; // 每次读取都强制回流
  box.style.left = `${width + i}px`;
}

// 先读取一次,缓存值
const baseWidth = box.offsetWidth;
let width = baseWidth;
for (let i = 0; i < 100; i++) {
  width = baseWidth + i; // 用缓存值计算,不触发回流
  ...
}

利用「文档碎片(DocumentFragment)」批量添加 DOM

文档碎片是「虚拟 DOM 容器」,修改碎片内的元素不会触发页面回流,最后一次性添加到页面:

ini 复制代码
// 创建文档碎片(离线容器)
const fragment = document.createDocumentFragment();
// 批量添加元素到碎片(无回流)
for (let i = 0; i < 100; i++) {
  const li = document.createElement('li');
  li.textContent = `Item ${i}`;
  fragment.appendChild(li);
}
// 一次性添加到页面(仅1次回流)
document.getElementById('list').appendChild(fragment);

使用 CSS3 硬件加速,减少回流 / 重绘

给元素添加transform/opacity(现代浏览器)等属性时,浏览器会将元素放到「GPU 层」渲染,避免触发全局回流:

css 复制代码
.box {
  position: absolute;
  top: 10px; /* 改top会触发回流 */
}

.box {
  transform: translateY(10px); /* 硬件加速,无回流 */
}

核心区别:

  • 回流是「布局重新计算」(耗性能),重绘是「外观重新渲染」(低消耗)
  • 回流必触发重绘,重绘不触发回流;

水平垂直居中的方式有哪些?

position: absolute + transform: translate

  • 传统方式,基于定位,这类方法兼容性最好,适用于所有现代浏览器和一些旧版浏览器。
  • 这是最常用、最灵活的方法之一,不需要知道元素的具体宽高。
css 复制代码
.parent {
  position: relative;
  width: 400px;
  height: 300px;
  border: 1px solid #ccc;
}

.child {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  
  width: 150px; /* 子元素的宽高可以是固定的,也可以是自适应的 */
  height: 100px;
  background-color: lightblue;
}

position: absolute + 负 margin

  • 一种经典方法,但是需要知道子元素的固定宽高
  • 兼容性极佳,元素脱离文档流。
css 复制代码
.child {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 150px;
  height: 100px;
  margin-top: -50px;  /* -height / 2 */
  margin-left: -75px; /* -width / 2 */
  background-color: lightcoral;
}

Flexbox 布局 (现代方法)

  • 现代 css 布局的首选方案,简单直观
  • 元素不脱离文档流,无需知道子元素宽高,还能处理更复杂的对齐和发布需求
css 复制代码
.parent {
  display: flex;
  justify-content: center; /* 水平居中 */
  align-items: center;    /* 垂直居中 */
  width: 400px;
  height: 300px;
  border: 1px solid #ccc;
}

Grid 布局 (更现代的方法)

Grid 布局是二维布局系统,同样可以非常简单地实现居中。

  • 元素不脱离文档流,代码更加简洁强大。
  • 但兼容性没那么高,不支持 IE10 及以下版本。
css 复制代码
.parent {
  display: grid;
  place-items: center; /* 水平垂直居中,是 align-items 和 justify-items 的简写 */
  width: 400px;
  height: 300px;
  border: 1px solid #ccc;
}

.child {
  width: 150px;
  height: 100px;
  background-color: lightsalmon;
}

其他

只作为拓展思路

  • margin: auto;
  • table: text-align + vertical-align
相关推荐
小林攻城狮2 小时前
前端实时语音转写:原生 MediaRecorder API 实践
前端·vue.js
Maxkim2 小时前
「✍️JS原子笔记 」零基础吃透 Proxy 数据响应式
前端·javascript·面试
踏浪无痕2 小时前
Java 17 升级避坑:如何安全处理反射访问限制
后端·面试·架构
hashiqimiya2 小时前
vue前端打包配置后端代理
前端
小白咚2 小时前
npm在文件下输入运行命令,授权限制问题window
前端·npm·node.js
AllinLin2 小时前
javaScript学习计划(Day26-30)
开发语言·javascript·学习
清平乐的技术专栏2 小时前
电脑自带Edge浏览器进行PDF文件合并
前端·edge·pdf
努力学算法的蒟蒻2 小时前
day48(12.29)——leetcode面试经典150
算法·leetcode·面试
Mintopia2 小时前
🌈 React-Markdown 教学指南 —— 写给想让网页诗意地“读懂” Markdown 的你 ✨
前端·react.js·markdown