likes
comments
collection
share

CSS高频面试题总结

作者站长头像
站长
· 阅读数 15

前言

整篇都是对面试过程中遇到过的CSS题目汇总,答案来自于网上各文档,亦或是看文档后的理解,若有不对的地方请指出,后续遇到新的题目也会持续补充。

1. 盒模型

页面中的每个元素都可以视为一个盒子,每个盒子由margin(外边距)、border(边框)、padding(内间距)、content(内容) 组成。如下图:

CSS高频面试题总结

盒模型包括标准盒模型、IE(怪异)盒模型两种,区别是它们对于content宽(高)度的计算方式的不同。具体如下:

.box {    
    width: 100px;    
    height: 100px;    
    padding: 10px;    
    border: 1px solid #eee;    
    margin: 10px; 
}
  • 标准盒模型中content宽(高)度 = width的值,即100px;
  • IE盒模型中,content宽(高)度的值 = width - padding -border,所以content的实际宽度为100 - 10 * 2 - 1 * 2 = 78px ;

标准盒模型设置box-sizing: border-box;转为IE盒模型,IE盒模型设置box-sizing: cotent-box;转为标准盒模型。

2. display: inline-block 为什么会有间隙?

因为HTML代码中的换行符、空格会被转成一个空白符,在字体不为0的情况下,空白符占据一定宽度,所以就出现了空隙。

解决方法
  • 设置font-size: 0
  • 移除换行和空格;

3. 文档流

标准文档流指块级元素一个一行地从上向下排列,行内元素从左往右排列的流程。脱离文档流指不遵循标准文档流的排列方式,即脱离文档流的元素不和标准文档流的元素在同一个层级(可以将每个层级理解为一个面)进行排列,而是将元素放在了一个新的层级,且不同层级内部的元素排列不会受到其它层级的影响。

  • 脱离文档流元素存在时的布局方式?
    • 使用float时,其它盒子会无视这个元素,但其它盒子内的文本依然会为这个元素让出位置,围绕在浮动元素周围;
    • 其他盒子在定位的时候,会当作脱离流的元素不存在而进行布局;

4. BFC 块级格式化上下文

Block Formatting Context。元素类型分为块元素、行元素、行内块元素,块级格式化上下文就是一个只有块元素参与独立的渲染区域,这个区域规定了内部元素如何布局,与区域外元素毫不相关。

BFC区域内布局规则
  1. 内部元素会在竖直方向依次排列;
  2. bfc区域不会与浮动元素区域重叠;
  3. 计算bfc元素的高度时,浮动元素也参与计算;
  4. 同一个bfc内的垂直方向两个元素之间的距离由margin决定,且两个相邻元素的margin会发生重叠;
  5. BFC是一个独立的容器,容器内部元素的排列不会影响到外部元素;
使用场景
  • 自适应两栏布局(bfc区域不会与浮动元素区域重叠);
  • 清除浮动(计算bfc元素的高度时,浮动元素也参与计算);
  • 解决margin重叠(同一个bfc内的垂直方向两个元素之间的距离由margin决定,且两个相邻元素的margin会发生重叠);
如何创建BFC
  • body根元素;
  • overflow的值不为visible;
  • position的值不为static和relative;
  • display的值为flex、inline-flex、inline-block;
关于margin重叠

margin重叠表现为当给两个相邻元素设置上下margin后,它们呈现出来的距离并不是两个元素的间距之和。通过下面例子来看: 给el1元素设置margin-bottom: 30px;,给el2元素设置margin-top: 20px;,正常情况下el1el2之间的距离应该是30px + 20px = 50px,但实际却是30px,这就是因为发生了margin重叠。

 <div class="wrap">
    <div class="el1">1</div>
    <div class="el2">2</div>
</div>

.wrap {
  overflow: hidden;
}

.el1 {
  width: 100px;
  height: 100px;
  background: red;
  margin-bottom: 30px;
}

.el2 {
  width: 200px;
  height: 200px;
  margin-top: 20px;
  background-color: aquamarine;
}

CSS高频面试题总结

这是因为在bfc区域内,对于这种情况有自己的计算规则,具体如下:

  • 都为正数,取两者中值大的
  • 一正一负,取正 - 负
  • 都为负数,取两者中绝对值较大的那个值

5. position定位

position用于设置元素在文档流中的位置,可通过top/bottom/left/right来改变元素的位置。

定位类型介绍
定位类型描述
static1. 无定位, 元素按照标准文档流排列;2. 设置z-index/top/left等属性不生效;
relative1. 绝对定位,元素按照标准文档流排列; 2. 设置top/left等属性是根据元素原来的位置进行定位;
absolute1. 绝对定位,元素脱离标准文档流; 2. 设置top/left等属性是根据最近的具有position属性且值不为static的祖先元素进行定位;
fixed1. 固定定位,元素脱离标准文档流; 2. 设置top/left等属性是根据浏览器视口进行定位; 3. 页面滚动时,元素不会跟随页面滚动
sticky1. 粘性定位,基于用户滚动的位置进行定位; 2. 设置top/left等属性是根据页面滚动高度进行定位。如position: sticky; top: 90px;表现为当页面滚动高度小于90时,元素表现为relative定位,滚动高度超过90时表现为fixed定位;
给定位元素同时设置left和right(top/bottom)是什么效果?

给定位元素同时设置left和right后,呈现效果要根据元素是否设置宽度来判断:

  • 设置了width属性,则right属性会被忽略;
  • 未设置width属性,则会拉伸这个元素去满足leftright的值。如设置了left: 0; right: 0;就相当于width: 100%;的效果;

给元素同时设置top和bottom同理,不过决定的是元素高度。根据这个特点可实现将一个定位元素设置为全屏:

.div {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
}

6. float 浮动

float最早是用于实现文字围绕图片排列功能。取值可为left、right、none, 分别代表向左浮动、向右浮动、不浮动。

浮动元素特性

当给元素设置float不为none时,浮动元素:

  • 允许文本或者内联元素围绕碰到父元素边框其它浮动元素停留
  • 脱离标准文档流,造成父元素高度塌陷
使用场景
  • 文本或者内联元素围绕着图片;
  • 两栏布局(代码见下文实现两栏布局部分);
清除浮动方式
  • 在浮动元素后面添加一个空元素如<div class="clear"></div>,并设置.clear{ clear: both; }
  • 给浮动元素的父元素设置overflow: hidden(利用BFC元素不和浮动元素重合特性);
  • 为浮动元素添加::after伪元素并添加clear属性,具体代码如下:
div::after {
    content: '';
    display: block;
    clear: both;
}
  • clear属性清除浮动的原理 使用clear并不是清除浮动,而是清除浮动所造成的影响。
div::after {
    content: '';
    display: block;
    clear: both;
}

以伪元素为例,设置了clear: both;后,伪元素不再允许周围有浮动的元素产生,所以浮动元素相对于伪元素也就失去了浮动的效果,浮动元素div对于伪元素来说也就有了高度,伪元素就会位于浮动元素的下面,伪元素的位置也就撑起了父级元素的高度,解决了高度坍塌的问题。

7. flex 弹性布局

该布局方式的主要思想是让父元素能够调整子元素的宽度、高度、排列方式,从而更好的适应可用的布局空间。当给元素设置display: flex后,这个元素就成为了一个flex容器,它所有子元素都会成为它的项目。flex容器默认有两条轴,一条是主轴,一条是与主轴垂直的交叉轴;

注意:设置了flex的元素,其内部元素的float、vertical-alignd都将失效。

flex容器常用属性
  • flex-direction:设置主轴的方向,决定了容器内项目的排列方式,默认是水平方向。
    • 可选值为<column | column-reverse | row | row-reverse>
  • flex-wrap:设置容器内项目是否允许换行,默认不允许换行。
    • 可选值nowrap | wrap | wrap-reverse
  • justify-content:设置项目在主轴上的对齐方式。
    • 可选值flex-start | flex-end | center | space-between | space-around
  • align-items:设置项目在交叉轴上的对齐方式。
    • 可选值flex-start | flex-end | center | baseline | stretch
  • align-content:设置多根轴线时的对齐方式,一般应用在容器内的项目不止一行的场景中。
    • 可选值flex-start | flex-end | center | baseline | stretch
项目常用属性
  • align-self:设置某一个项目在交叉轴上的对齐方式。
  • flex-shrink:设置是否允许该项目缩小。
  • flex-grow:设置是否允许该项目放大。
  • flex-basis:设置项目占据主轴的空间的大小。浏览器会按照该属性来判断容器是否有剩余空间,从而决定放大或缩小项目。
  • flex:是flex-growflex-shrink 和 flex-basis的简写,默认值0 1 auto
flex-basis 详解
特性
  • 当主轴为水平时,该属性的值代表每个项目的宽度;主轴为竖轴时,该属性的值代表每个项目的高度,这也是它跟width和height不一样的地方。
  • 当给一个元素同时设置了flex-basis和width时,flex-basis的优先级更高。
  • 浏览器会根据该属性来判断flex容器是否有剩余空间,从而决定放大或缩小项目。
取值说明

该属性取值可为<auto | 0% | length>

  • flex: auto:长度等于项目(设置的width/height)的长度。如果项目未指定长度(未设置width或height属性),则长度就是元素内容撑开的长度。
  • flex: 0%:不代表项目的最终宽度为0,而是代表会忽略元素设置的width或 height属性。当flex容器有剩余空间时,会根据剩余空间和flex-grow属性的值为该元素分配宽度。
  • flex-basis: 100px:即直接设置项目的宽度为100px。
flex: 1 指什么?

flex是flex-growflex-shrinkflex-basis的缩写。三者之间的关系如下:

  • 首先根据flex-basis的值来计算flex容器是否有剩余空间;
  • 当flex容器有剩余空间时,则根据flex-grow来放大项目;
  • 若无剩余空间且空间不能排列所有的项目时,则根据flex-shrink来缩小项目。
.parent {
    display: flex;
    width: 600px;
}
.parent > div {
    height: 100px;
}
.item-1 {
    width: 140px;
    flex: 1 1 0%;
    background: blue;
}
.item-2 {
    width: 100px;
    flex: 2 1 auto;
    background: darkblue;
}
.item-3 {
    flex: 1 1 200px;
    background: lightblue;
}

<div class="parent">
    <div class="item-1"></div>
    <div class="item-2"></div>
    <div class="item-3"></div>
</div>

主轴上flex容器总尺寸为600px。所有子项目的总基准值是:0% + auto + 200px = 300px,其中

  • 0% 即 0 宽度;
  • auto 对应取原尺寸即 100px

故剩余空间为600px - 300px = 300px,伸缩放大系数之和为1 + 2 + 1 = 4。剩余空间分配如下:

item-1item-3 各分配 1/4,各得 75px item-2 分配 2/4,得 150px

各项目最终宽度为:

item-1 = 0% + 75px = 75px item-2 = auto + 150px = 250px item-3 = 200px + 75px = 275px

8. CSS层叠上下文、层叠等级、层叠顺序

层叠上下文(stacking context)

首先要知道我们浏览的一个网页不只是由一层构成的。如下图中的情况,a和c、d两个元素发生了重叠,为了呈现a盖住c、d的效果,所以需要先绘制c和d再绘制a。因此浏览器在绘制之前将会对这三个元素进行分层,a在一个层级,c和d属于同一个层级。分完后会先绘制b所在的层级的所有元素,再绘制a所在的层级的所有元素,这样就实现了a盖住c、d的效果。分层的另外一个好处就是如果发生重绘,则只会重绘元素所在的层级,不会影响到其它图层。那么浏览器是根据什么对元素进行分层呢?就是层叠上下文。

CSS高频面试题总结

层叠上下文是html中的一个三维概念,即每个盒模型的位置是三维的,分别是平面画布上的X轴Y轴以及表示层叠的Z轴。元素在页面上沿X、Y轴平铺,层叠上下文按照既定规则在Z轴排列。如果一个元素含有层叠上下文,表现为它离屏幕观察者更近

CSS高频面试题总结

如何产生层叠上下文
  • html根元素就是一个默认的层叠上下文,称为“根层叠上下文”;
  • 设置了position值不为static,且具有z-index的值为数值;
  • CSS3中的一些新属性:
    • 父元素的display属性值为flex|inline-flex,子元素z-index属性值不为auto的时候,子元素为层叠上下文元素;
    • 元素的opacity属性值不是1
    • 元素的transform属性值不是none
    • 元素的filter属性值不是none
    • 元素的isolation属性值是isolate
    • will-change指定的属性值为上面任意一个;
    • 元素的-webkit-overflow-scrolling属性值设置为touch
层叠等级
  • 在同一个层叠上下文中,它定义了该上下文中的层叠上下文元素在Z轴的上下顺序。例:
<style>
  div {  
    position: relative;  
    width: 100px;  
    height: 100px;  
  }  
  p {  
    position: absolute;  
    font-size: 20px;  
    width: 100px;  
    height: 100px;  
  }  
  .a {  
    background-color: blue;  
    z-index: 1;  
  }
  
  .b {  
    background-color: green;  
    z-index: 2;  
    top: 20px;  
    left: 20px;  
  }
  
  .c {  
    background-color: red;  
    z-index: 3;  
    top: -20px;  
    left: 40px;  
  }
</style>

<body>  
  <div>  
    <p class="a">a</p>  
    <p class="b">b</p>  
  </div> 
  <div>  
    <p class="c">c</p>  
  </div>  
</body> 

因为p.a、p.b、p.c三个的父元素div都没有设置z-index,所以不会产生层叠上下文,所以.a、.b、.c都处于由<html></html>标签产生的“根层叠上下文”中,属于同一个层叠上下文,此时谁的z-index值大,谁在上面。

  • 层叠等级的比较只有在当前层叠上下文元素中才有意义。不同层叠上下文中比较层叠等级是没有意义的。例:
<style>
  div {  
    position: relative;  
    width: 100px;  
    height: 100px;  
  }
  
  .box1 {
      z-index: 2;
    }

    .box2 {
      z-index: 1;
    }
  
  p {  
    position: absolute;  
    font-size: 20px;  
    width: 100px;  
    height: 100px;  
  }  
  
  .a {  
    background-color: blue;  
    z-index: 2;  
  }
  
  .c {  
    background-color: red;  
    z-index: 999;  
    top: -20px;  
    left: 40px;  
  }
</style>

<body>  
  <div class="box1">  
    <p class="a">a</p>  
  </div> 
  <div class="box2">  
    <p class="c">c</p>  
  </div>  
</body> 

CSS高频面试题总结 分别给box1 box2设置了z-index,因此a、c分别属于box1、box2层叠上下文。从图可以看出虽然a元素的z-index值小于c元素的z-index的值,但a元素仍然在c元素的上面。这就是因为a所在的层叠上下文box1的层级 高于 c所在的层叠上下文box2的层级,所以比较a和c的层级没有意义。

  • 在普通元素中,它描述的是普通元素在Z轴的上下顺序。普通元素的层叠等级优先由其所在的层叠上下文决定
层叠顺序(stacking order)

层叠顺序是指在同一个层叠上下文中,元素在Z轴上显示的顺序。具体层叠顺序如下图: CSS高频面试题总结

为什么inline/inline-block元素的层叠顺序要高于block/float元素? 因为像border/background属于装饰元素的属性,浮动和块级元素一般用来页面布局,而网页设计之初最重要的就是文字内容,所以在发生层叠时会优先显示文字内容,保证其不被覆盖。

层叠元素后排列规则总结

首先比较的两个元素是否处于同一个层叠上下文:

  • 如果属于同一层叠上下文,谁的层叠等级大,谁在上面(根据“层叠顺序”图来判断);
  • 如果不属于同一层叠上下文,则比较它们所处的层叠上下文的层叠等级;
    • 当两个元素层叠等级相同、层叠顺序相同时,在DOM结构中后面的元素层叠等级在前面元素之上。

9. 伪类和伪元素

  • 伪类:是一种选择器,选择处于特定状态的元素;以:开头;如鼠标hover
  • 伪元素:是一个虚拟元素,但不存在dom中;以::开头;常用::after、::before来实现清除浮动、绘制三角形等功能;

10. 隐藏元素的方式

  • visibility: hidden:元素视觉上不可见,但占用页面空间,可以进行交互;
  • display: none: 元素会不可见,并且从文档流中消失,不再占用页面空间;
  • opacity: 0:将元素的透明度设置为0,占据空间且可以进行交互;
  • position: absolute:通过设置left/top在页面外,使元素位置在可见区域之外;
  • transform: scale(0): 将一个元素设置为缩放0,元素将不可见,元素原来所在的位置将被保留,不可交互;
  • height: 0

11. 移动端适配方案

  • rem:以html根元素的font-size大小来进行适配。
<script>
function setRem() { 
    // 当前页面宽度相对于 750 宽的缩放比例
    const scale = document.documentElement.clientWidth / 750; 
    document.documentElement.style.fontSize = scale + 'px';
} 
setRem(); 
</script>
  • vw/vh
  • 百分比布局
  • 通过媒体查询,针对不同的屏幕大小单独设置
  • px 为主,搭配 vw/vh、媒体查询与 flex 进行布局

12. 如何解决移动端边框1px问题?

屏幕分辨率:屏幕在纵横向上的像素点数。相同屏幕尺寸,分辨率越大越清晰。

解决方式
  1. 直接给border的宽度设置为0.5,缺点是安卓系统不能使用且ios8不兼容;
  2. 用图片代替边框,缺点是无法实现圆角;
border: 1px solid transparent; 
border-image: url('xxx.jpg') 2 repeat;
  1. 伪元素先放大,在使用scale缩放;
.div {
  position: relative;
  
  &::after {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    height: 1px;
    width: 100%;
    transform: scaleY(0.5);
    transform-origin: left top;
    background-color: red;
  }
}
  1. box-shadow模拟边框实现;
box-shadow: inset 0px -1px 1px -1px #c8c7cc;

代码实现

1. 实现两栏布局

两栏布局左边宽度固定,右边元素宽度自适应。有以下4种方式:

  • 方式1: 左元素(200px)浮动,右元素通过margin-left: 200px;来让右边元素偏移200px;
<div class="parent">
    <div class="left"></div>
    <div class="right"></div>
</div>

.parent {
  height: 100px;
  position: relative;
}

.left {
  float: left;
  width: 200px;
  height: 100px;
}

.right {
  width: 200px;
  height: 100px;
  // 偏移左边200px
  margin-left: 200px;
}
  • 方式2: 左元素absolute脱离文档流,右元素不设置宽度,设置left: 200px; right: 0,原理见上文position同时设置left和right原理:
.parent {
  height: 100px;
  position: relative;
}

.left {
  position: absolute;
  top: 0;
  left: 0;
  width: 200px;
  height: 100px;
}

.right {
  position: relative;
  left: 200px;
  right: 0;
}
  • 方式3: 左元素浮动,右元素设置为BFC元素,利用BFC元素不和浮动元素重叠特性:
.parent {
  height: 100px;
}

.left {
  float: left;
  width: 200px;
  height: 100px;
  background: red;
}

.right {
  height: 100%;
  background: blue;
  overflow: hidden;
}
  • 方式4: flex布局利用flex: 1
.parent {
  height: 100px;
  display: flex;
}

.left {
  width: 200px;
  height: 100px;
  background: red;
}

.right {
  flex: 1;
  height: 100%;
  background: blue;
}

2. 实现三栏布局

三栏布局指左右两栏宽度固定,中间宽度自适应

  • 方式1: 左元素左浮动,中间元素设置为BFC元素,右元素右浮动:

这种情况下要注意right元素放在center元素前面,这样right元素才能浮动。

// right 要放center元素前面,不然center元素会挡住right元素浮动
<div class="parent">
  <div class="left"></div>
  <div class="right"></div>
  <div class="center"></div>
</div>

.left {
  float: left;
  width: 200px;
  height: 100px;
  background: red;
}

.center {
  height: 100%;
  background: blue;
  overflow: hidden;
}

.right {
  float: right;
  width: 200px;
  height: 100px;
  background: green;
}
  • 方式2: 左右元素都absolute,中间元素设置左右margin为左右元素的宽度(right元素也要放在center元素前面):
<div class="parent">
  <div class="left"></div>
  <div class="right"></div>
  <div class="center"></div>
</div>

.left {
  position: absolute;
  left: 0;
  width: 200px;
  height: 100px;
  background: red;
}

.center {
  margin-left: 200px;
  margin-right: 200px;
  height: 100%;
  background: blue;
}

.right {
  position: absolute;
  right: 0;
  width: 200px;
  height: 100px;
  background: green;
}
  • 方式3: 利用flex,同两栏布局一样。

3. 实现如下布局

CSS高频面试题总结 思路:利用flex布局,且为每个元素单独设置align-self属性,改变其对齐方式。

<div class="box1">
    <div class="a">a</div>
    <div class="b"></div>
    <div class="c"></div>
</div>
  
.box1 {
  display: flex;
  flex-direction: column;
}

.a, .b, .c {
  width: 200px;
  height: 50px;
  margin-bottom: 20px;
  background: red;
}
.b {
  align-self: center;
}

.c {
  align-self: flex-end;
}

4. 用动画实现将一个div从左边移动到右边

 @keyframes toRight {
  0% {
    margin-left: 0px;
  }

  100% {
    margin-left: 600px;
  }
}

.parent {
  width: 100px;
  height: 100px;
  background: red;
  animation: toRight 1s infinite;
}

5. 使用css实现三角形

原理:border是由三角形组成的;给一个元素不设置宽高只设置border的时候,表现如下

CSS高频面试题总结

.parent {
  width: 100px;
  height: 100px;
  border: 1px solid black;
  position: relative;
}

.parent::before {
  content: '';
  display: block;
  width: 0;
  height: 0;
  position: absolute;
  border-left: 50px solid red;
  border-top: 50px solid red;
  border-bottom: 50px solid transparent;
  border-right: 50px solid transparent;
}

6. 实现等边三角形

CSS高频面试题总结 思路: 假定等边三角形的高度为40,再根据等边三角形的每个角都为60度,则有sin(60) = 高度(40) / 边长,可以计算出来边长为23左右。再使用border绘制三角形即可。

.parent {
  width: 0px;
  height: 0px;
  border-left: 23px solid transparent;
  border-right: 23px solid transparent;
  border-bottom: 40px solid red;
}

7. 实现一个扇形

CSS高频面试题总结 思路: 一边的border + border-radius 50%

.parent {
  width: 0;
  height: 0;
  border: 100px solid transparent;
  border-top-color: red;
  border-radius: 50%;
}

8. 实现一个梯形

CSS高频面试题总结 思路:元素本身一个宽度实现矩形区域 + border实现三角形;

.parent {
   width: 100px;
   height: 0;
   border-bottom: 100px solid red;
   border-right: 40px solid transparent;
}

9. 实现文字多行打点展示

.parent{
  width: 200px;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

10. 实现一个元素满屏展示

思路:见上文给一个元素同时设置top/bottom和left/top。

.parent {
  position: fixed;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  background-color: pink;
}

11. CSS实现元素固定宽高比

  • 方式1: aspect-ratio
  /* 宽/高  */
  aspect-ratio: 16 / 9;
  • 方式2: padding-bottom

原理: 垂直方向上的内外边距为百分比时,是按父元素的宽度进行计算

.wrap {
  width: 800px;
}

.child {
  width: 400px;
  height: 0;
  /* 元素显示出来的高度为800 * 10% = 80 */
  padding-bottom: 10%;
}

参考文章: