轻松搞定CSS布局以及高频面试题

404 阅读12分钟

布局的基础

盒子模型

盒子模型:每个元素,都会形成一个矩形块,主要包括四部分:margin(外边距)+border(边框)+padding(内边距)+content(内容)

盒子模型又可以分为标准盒子模型和IE盒子模型,区别在于设置的宽高包含哪些部分

标准盒子模型

image.png

IE盒子模型

image.png

那既然有两种盒子模型,那么有方法可以改变吗?

当然存在,box-sizing属性可以设置。

box-sizing: content-box; // 指定盒子模型为W3C(标准盒模型)

box-sizing: border-box; // 指定盒子模型为IE盒子模型

box-sizing: inherit;// 规定应从父元素继承 box-sizing 属性的值。

标准文档流 (Normal Flow)和 浮动

标准文档流

标准文档流(normal flow)是指在不对页面进行任何布局控制时,浏览器默认的HTML布局方式。其中块级元素从上到下,行内元素从左到右进行渲染。

<!DOCTYPE html>
<html lang="">
  <head>
    <style>
      .one {
        width: 200px;
        height: 60px;
        background-color: #67c23a;
        
      }

      .span-one{
        background-color: blue;
      }

      .span-two{
        background-color: red;
      }
      
      .two {
        width: 100px;
        height: 60px;
        background-color: #e6a23c;
      }

      .three {
        width: 100px;
        height: 60px;
        background-color: #f56c6c;
      }
    </style>
  </head>
  <body>
    <div class="one">
      <span class="span-one">span1</span>
      <span class="span-two">span2</span>
    </div>
    <div class="two">two</div>
    <div class="three">three</div>
    
  </body>
</html>

显示如图:

image.png

浮动(float)

浮动(float)就如字面上的意思让元素浮起来,脱离它原来的位置,那么剩下的元素就会改变它的位置,使得整个布局发生变化。

<!DOCTYPE html>
<html lang="">
  <head>
    <style>

      .one {
        width: 200px;
        height: 60px;
        background-color: #67c23a;
        
      }

      .span-one{
        background-color: blue;
      }

      .span-two{
        // 设置浮动
        float: left;
        background-color: red;
      }

      .two {
        width: 100px;
        height: 60px;
        background-color: #e6a23c;
      }

      .three {
        
        width: 100px;
        height: 60px;
        background-color: #f56c6c;
      }
    </style>
  </head>
  <body>
      <div class="one">
        <span class="span-one">span1</span>
        <span class="span-two">span2</span>
      </div>
      <div class="two">two</div>
      <div class="three">three</div>
  </body>
</html>

显示如图:

image.png

提到浮动,那么我们就会想起我们面试常被问到的 清除浮动 这个问题,也就是让我们将浮动元素限制在可控的范围内,不影响其他的元素的排布。

清除浮动

设置浮动前

<!DOCTYPE html>
<html lang="">
  <head>
    <style>
      .box{
        background-color: red;
      }

      .one {
        width: 200px;
        height: 60px;
        background-color: #67c23a;
      }

      .two {
        width: 100px;
        height: 120px;
        background-color: #e6a23c;
      }
    </style>
  </head>
  <body>
    <div class="box">
      <div class="one">one</div>
      <div class="two">two</div>
    </div>
  </body>
</html>

image.png

设置浮动后

<!DOCTYPE html>
<html lang="">
  <head>
    <style>
      .box{
        background-color: red;
      }
      
      .one {
        float: left;
        width: 200px;
        height: 60px;
        background-color: #67c23a;    
      }
      
      .two {
        float: left;
        width: 100px;
        height: 120px;
        background-color: #e6a23c;
      }
    </style>
  </head>
  <body>
    <div class="box">
      <div class="one">one</div>
      <div class="two">two</div>
    </div>
  </body>
</html>

image.png

方法1:父级设置高度(不推荐)

<!DOCTYPE html>
<html lang="">
  <head>
    <style>
      .box{
        height: 150px;
        background-color: red;
      }

      .one {
        float: left;
        width: 200px;
        height: 60px;
        background-color: #67c23a;    
      }

      .two {
        float: left;
        width: 100px;
        height: 120px;
        background-color: #e6a23c;
      }
    </style>
  </head>
  <body>
    <div class="box">
      <div class="one">one</div>
      <div class="two">two</div>
    </div>
  </body>
</html>

image.png

方法2:父级设置 overflow: hidden(不推荐)

通过触发BFC方式,实现清除浮动

<!DOCTYPE html>
<html lang="">
  <head>
    <style>
      .box{
        background-color: red;
        overflow: hidden;
      }

      .one {
        float: left;
        width: 200px;
        height: 60px;
        background-color: #67c23a;    
      }

      .two {
        float: left;
        width: 100px;
        height: 120px;
        background-color: #e6a23c;
      }
    </style>
  </head>
  <body>
    <div class="box">
      <div class="one">one</div>
      <div class="two">two</div>
    </div>
  </body>
</html>

image.png

上述的两种方法本质上就是通过父级元素去包裹住浮动的子元素让其浮动不去影响到其他元素。

方法3:额外标签设置 clear:both(不推荐)

<!DOCTYPE html>
<html lang="">
  <head>
    <style>
      .box{
        background-color: red;
      }

      .one {
        float: left;
        width: 200px;
        height: 60px;
        background-color: #67c23a;    
      }

      .two {
        float: left;
        width: 100px;
        height: 120px;
        background-color: #e6a23c;
      }
      
      .clear {
        clear: both;
        background-color: blue;
      }
    </style>
  </head>
  <body>
    <div class="box">
      <div class="one">one</div>
      <div class="two">two</div>
      // 添加额外的标签
      <div class="clear">three</div>
    </div>
  </body>
</html>

image.png

方法4:伪元素配合 clear:both(推荐)

<!DOCTYPE html>
<html lang="">
  <head>
    <style>
      .box{
        background-color: red;
      }
      // 伪元素
      .box:after{
        content: "";
        display: block;
        clear:both;
        visibility: hidden;
      }

      .one {
        float: left;
        width: 200px;
        height: 60px;
        background-color: #67c23a;    
      }

      .two {
        float: left;
        width: 100px;
        height: 120px;
        background-color: #e6a23c;
      }
    </style>
  </head>
  <body>
    <div class="box">
      <div class="one">one</div>
      <div class="two">two</div>
    </div>
  </body>
</html>

image.png

BFC

BFC(Block Formatting Context)即块级格式化上下文。和上面提到的盒子模型结合,它就是一个起到隔离作用的独立的盒子,盒子中的子元素任意操作都不会超出这个盒子去影响其他盒子。

特性

  1. 块级元素会在垂直方向一个接一个的排列,和文档流的排列方式一致。
  2. 在 BFC 中上下相邻的两个容器的 margin  会重叠,创建新的 BFC 可以避免外边距重叠。
  3. 计算 BFC 的高度时,需要计算浮动元素的高度。
  4. BFC 区域不会与浮动的容器发生重叠。
  5. BFC 是独立的容器,容器内部元素不会影响外部元素。
  6. 每个元素的左 margin  值和容器的左 border  相接触。
  • 利用 4  和 6 ,我们可以实现三栏(或两栏)自适应布局。
  • 利用 2 ,我们可以避免 margin  重叠问题。
  • 利用 3 ,我们可以避免高度塌陷,实现清除浮动

BFC 的创建

以下元素会创建 BFC

  • 根元素(<html>
  • 浮动元素(float 不为 none
  • 绝对定位元素(positionabsolutefixed
  • 表格的标题和单元格(displaytable-captiontable-cell
  • 匿名表格单元格元素(displaytableinline-table
  • 行内块元素(displayinline-block
  • overflow 的值不为 visible 的元素
  • 弹性元素(displayflexinline-flex 的元素的直接子元素)
  • 网格元素(displaygridinline-grid 的元素的直接子元素)

弹性盒子(Flexbox)

Flexbox 是CSS 弹性盒子布局模块(Flexible Box Layout Module)的缩写,它被专门设计出来用于创建横向或是纵向的一维页面布局。要使用flexbox,你只需要在想要进行flex布局的父元素上应用display: flex ,所有直接子元素都将会按照flex进行布局。

采用 Flex 布局的元素,称为 Flex 容器(flex container),简称"容器"。它的所有子元素自动成为容器成员,称为 Flex 项目(flex item),简称"项目"。

image.png

容器默认存在两根轴:水平的主轴(main axis)和垂直的交叉轴(cross axis)。主轴的开始位置(与边框的交叉点)叫做main start,结束位置叫做main end;交叉轴的开始位置叫做cross start,结束位置叫做cross end。 项目默认沿主轴排列。单个项目占据的主轴空间叫做main size,占据的交叉轴空间叫做cross size。

容器的属性(也就是包裹的父元素属性)

  • flex-direction:决定主轴的方向。
  • flex-wrap:如果一条轴线排不下,如何换行。
  • flex-flow:flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。
  • justify-content:项目在主轴上的对齐方式。
  • align-items:项目在交叉轴上如何对齐。
  • align-content:多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用

项目的属性(也就是子元素属性)

  • order:项目的排列顺序。数值越小,排列越靠前,默认为0。
  • flex-grow:项目的放大比例,默认为0,即如果存在剩余空间,也不放大。
  • flex-shrink:项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。
  • flex-basis:在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。
  • flex:是flex-growflex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选。该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)。
  • align-self:允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch

具体的展开可以去看阮一峰写的Flex

flex:1和flex:0代表什么

我们知道项目的属性 flexautonone 两种比常用的快捷值,那么flex:1和flex:0又是代表什么呢

<!DOCTYPE html>
<html lang="">
  <head>
    <style>
      .box{
        display: flex;
        background-color: red;
      }

      .one {
        flex: 1 1 auto;
        width: 10px;
        height: 60px;
        background-color: #67c23a;    
      }

      .two {
        flex: 2 1 auto;
        width: 150px;
        height: 120px;
        background-color: #e6a23c;
      }
      
      /* 第二种情况
      .one {
        flex: 1;
        width: 10px;
        height: 60px;
        background-color: #67c23a;    
      }

      .two {
        flex: 2;
        width: 150px;
        height: 120px;
        background-color: #e6a23c;
      } */

      .three {
        width: 100px;
        height: 120px;
        background-color: #613ce6;
      }

    </style>
  </head>
  <body>
    <div class="box">
      <div class="one">one</div>
      <div class="two">two</div>
      <div class="three">three</div>
    </div>
  </body>
</html>

flex:1 1 auto 显示 image.png

flex:1 显示 image.png

看上图再结合w3c中对于flex的解释,那么flex:1就是flex:1 1 0%表示子元素的宽度直接将设置flex的子元素和剩余宽度加起来然后按比例分配放大填充,felx:2的子元素和flex:1的子元素比就是2:1,如果子元素都设置flex:1,那么就是所有子元素均分

<!DOCTYPE html>
<html lang="">
  <head>
    <style>
      .box{
        display: flex;
        background-color: red;
      }

      .one {
        flex: 0 1 auto;
        width: 50px;
        height: 60px;
        background-color: #67c23a;    
      }

      .two {
        flex: 0 1 auto;
        width: 150px;
        height: 120px;
        background-color: #e6a23c;
      }
      
      /* 第二种情况
      .one {
        flex: 0;
        width: 50px;
        height: 60px;
        background-color: #67c23a;    
      }

      .two {
        flex: 0;
        width: 150px;
        height: 120px;
        background-color: #e6a23c;
      } */

      .three {
        width: 100px;
        height: 120px;
        background-color: #613ce6;
      }

    </style>
  </head>
  <body>
    <div class="box">
      <div class="one">one</div>
      <div class="two">two</div>
      <div class="three">three</div>
    </div>
  </body>
</html>

flex:0 1 auto 显示 image.png

flex:0 显示 image.png

那么flex:0就是flex:0 1 0%表示子元素不管本身的宽度如何直接缩放到最小的内容宽度

Grid布局

网格是一组相交的水平线和垂直线,它定义了网格的列和行。

CSS 提供了一个基于网格的布局系统,带有行和列,可以让我们更轻松地设计网页,而无需使用浮动和定位。

它将网页划分成一个个网格,可以任意组合不同的网格,做出各种各样的布局。

Flex 布局是轴线布局,只能指定"项目"针对轴线的位置,可以看作是一维布局。Grid 布局则是将容器划分成"行"和"列",产生单元格,然后指定"项目所在"的单元格,可以看作是二维布局。

下图这样的布局,就很适合用Grid网格布局实现。

image.png

采用网格布局的区域,称为"容器"(container)。容器内部采用网格定位的子元素,称为"项目"(item)。

注意:项目只能是容器的顶层子元素,不包含项目的子元素。Grid 布局只对项目生效。

容器里面的水平区域称为"行"(row),垂直区域称为"列"(column)。

容器的属性(也就是包裹的父元素属性)

  • grid-template-columns:定义每一列的列宽。
  • grid-template-rows:定义每一行的行高。
  • grid-row-gap:设置行与行的间隔(行间距)。
  • grid-column-gap:设置列与列的间隔(列间距)。
  • grid-gap:是grid-row-gapgrid-column-gap的合并简写形式,省略第二个列间距表示行列间距相同。
  • grid-template-areas:定义区域。
  • grid-auto-flow:设置容器的子元素会按照顺序,默认值row:先行再列,column:先列再行,就是横着排还是竖着排。
  • justify-items:设置单元格内容的水平位置。
  • align-items:设置单元格内容的垂直位置。
  • place-items:是align-items属性和justify-items属性的合并简写形式,省略第二个表示水平位置和垂直位置设置一样。
  • justify-content: 整个内容区域在容器里面的水平位置。
  • align-content: 整个内容区域的垂直位置。
  • place-content:是align-content属性和justify-content属性的合并简写形式,省略第二个表示水平位置和垂直位置设置一样。
  • grid-auto-columnsgrid-auto-rows:置,浏览器自动创建的多余网格的列宽和行高。

项目的属性(也就是子元素属性)

  • grid-column-start:左边框所在的垂直网格线。
  • grid-column-end:右边框所在的垂直网格线。
  • grid-row-start:上边框所在的水平网格线。
  • grid-row-end:下边框所在的水平网格线。
  • grid-column:是grid-column-startgrid-column-end的合并简写形式。
  • grid-row:是grid-row-start属性和grid-row-end的合并简写形式。
  • grid-area:指定项目放在哪一个区域。
  • justify-self:设置单元格内容的水平位置。
  • align-self:设置单元格内容的垂直位置。
  • place-self:是align-items属性和justify-items属性的合并简写形式,省略第二个表示水平位置和垂直位置设置一样。

具体的展开可以去看阮一峰写的Grid

常见布局实现

垂直水平居中

方法1:position定位

<div class="center">center</div>

// 宽和高要知道,设置margin要取一半
.center {
    background: red;
    width: 100px;
    height: 100px;
    position: absolute;
    top: 50%;
    left: 50%;
    margin: -50px 0 0 -50px;
} 
<div class="center">center</div>

.center {
    color: red;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%,-50%);
}

方法2:flex布局

<div class="wrap">
  <div class="item">center</div>
</div>

// 父元素
.wrap {
    width: 100%;
    height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
}
// 子元素在父元素中垂直居中
.item {
    color: red;
}

方法3: 表格对齐

<div class="wrap">
  <div class="item">center</div>
</div>

// 父元素
.wrap {
    width: 300px;
    height: 300px;
    display: table-cell;
    text-align: center;
    vertical-align: middle;
}
// 子元素在父元素中垂直居中
.item {
    width: 100px;
    height: 100px;
    // 必须为行内元素
    display: inline-block;
}

方法4:grid布局

<div class="wrap">
  <div class="item">center</div>
</div>

// 父元素
.wrap {
    width: 300px;
    height: 300px;
    display: grid;
}
// 子元素在父元素中垂直居中
.item {
    width: 100px;
    height: 100px;
    align-self: center;
    justify-self: center;
}

两列布局(左侧定宽,右侧自适应)

方法1:float + margin

<div class="left">定宽</div>
<div class="right">自适应</div>
  
// 左边
.left {
    float:left;
    width: 300px;
    height: 100%;
}
// 右边
.right {
    // 大于等于左边的宽度
    margin-left:300px;
    height: 100%;
}

方法2:position定位 + margin

<div class="parent">
   <div class="left">定宽</div>
   <div class="right">自适应</div>
</div>
   
  
// 父级
.parent {
    position: relative;
    height: 300px;
}
// 左边
.left {
    position: absolute;   
    top: 0;   
    left: 0;
    width: 300px;
    height: 100%;
}
// 右边
.right {
    // 大于等于左边的宽度
    margin-left: 300px;
    height: 100%;
}

方法3:flex布局

<div class="parent">
   <div class="left">定宽</div>
   <div class="right">自适应</div>
</div>
   
// 父级
.parent {
    height: 300px;
    display: flex;
}
// 左边
.left {
    width: 300px;
    height: 100%;
}
// 右边
.right {
    flex: 1;
    height: 100%;
}

三列布局

方法1:float + margin

<div class="left">左栏</div>
<div class="right">右栏</div>
<div class="middle">中间栏</div>

// 左边
.left {
    float: left;
    width: 200px;
    height: 300px;
}
// 右边
.right {
    float: right;
    width: 150px;
    height: 300px;
}
// 中间
.middle {
    height: 300px;
    // 大于等于左边的宽度
    margin-left: 220px;
    // 大于等于右边的宽度
    margin-right: 160px;
}

方法2:position定位 + margin

<div class="parent">
    <div class="left">左栏</div>
    <div class="right">右栏</div>
    <div class="middle">中间栏</div>
</div>

// 父级
.parent {
    position: relative;
    height: 300px;
}
// 左边
.left {
    position: absolute;
    top: 0;
    left: 0;
    width: 200px;
    height: 100%;
}
// 右边
.right {
    position: absolute;
    top: 0;
    right: 0;
    width: 150px;
    height: 100%;
}
// 中间
.middle {
    height: 100%;
    // 大于等于左边的宽度
    margin-left: 220px;
    // 大于等于右边的宽度
    margin-right: 160px;
}

方法3:flex布局

<div class="parent">
    <div class="left">左栏</div>
    <div class="middle">中间栏</div>
    <div class="right">右栏</div>
</div>

// 父级
.parent{
    display:flex;
    height: 300px;
}
// 左边
.left {
    width: 200px;
    height: 100%;
}
// 中间
.middle {
    flex: 1;
    height: 100%;
}
// 右边
.right {
    width: 150px;
    height: 100%;
}

圣杯布局(三列布局衍生,主要内容元素middle优先渲染)

<div class="parent">
    <div class="middle">中间栏</div>
    <div class="left">左栏</div>
    <div class="right">右栏</div>
</div>


// 父级
.parent{
    padding-left: 200px;  /* 预留左侧空间,为.left宽度*/
    padding-right: 150px; /* 预留左侧空间,为.right宽度*/
}
// 中间
.middle {
    float: left;
    width: 100%;
    height: 300px;
}
// 左边
.left {
    float: left;
    margin-left: -100%;   /* 移动到左侧,100%是一个父元素宽度,这里也就是.parent的宽度 */
    position: relative;   /* 因为.parent设置了padding*/
    right: 200px;     /* 所以需要再向左移动自身宽度,left: -200px;也是可以的 */
    width: 200px;
    height: 300px;
}
// 右边
.right {
    float: left;
    margin-right: -150px; /* 移动到右侧,自身宽度*/
    width: 150px;
    height: 300px;
}

双飞翼布局(和圣杯区别,中间栏外面再包裹一层)

<div class="parent">
    <div class="middle">中间栏</div>
</div>
<div class="left">左栏</div>
<div class="right">右栏</div>


// 父级
.parent{
    float:left;
    width: 100%;
    height: 300px;
}
// 中间
.middle {
    margin-left: 200px;  /* 预留左侧空间,为.left宽度*/
    margin-right: 150px; /* 预留左侧空间,为.right宽度*/
    height: 300px;
}
// 左边
.left {
    float: left;
    margin-left: -100%;   /* 移动到左侧,100%是一个父元素宽度*/
    width: 200px;
    height: 300px;
}
// 右边
.right {
    float: left;
    margin-left: -150px; /* 移动到右侧,自身宽度*/
    width: 150px;
    height: 300px;
}

参考资料