Skip to content

Flex 布局(三) ​

本页对应 Flex 布局,共 7 段视频,约 1 小时 3 分。

学习目标 ​

  • 能独立完成小米右侧 8 个小盒子的结构与样式,设置每个小盒子 234×300,并解释 978 宽度下 4 个一行的计算依据。
  • 能复现并解释浮动元素脱标导致父容器高度塌陷、下方盒子上升的现象,并说明其产生原因。
  • 能使用额外标签法、单/双伪元素法、父级 overflow: hidden 等方式为指定父容器清除浮动,并能识别常见 clearfix 写法。
  • 能说明浮动的本质是图文混排,并使用 float 实现图片被文字环绕的效果。
  • 能使用 display: flex 将子元素排列到一行,并说明弹性容器、弹性盒子、主轴与侧轴的基本概念。
  • 能使用 justify-content 的 flex-start、flex-end、center、space-between、space-around、space-evenly 实现指定主轴排列,并区分不同间距分布规律。

知识点 ​

浮动的基本使用 ​

为什么需要:块元素默认从上往下排,想让多个块元素水平排列时,可以用浮动。怎么想:给需要并排的盒子加 float,left 左对齐,right 右对齐。怎么做:属性名是 float,属性值常用 left、right。示例代码保留如下:

html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>浮动</title>
  <style>
    .left,
    .right {
      /* width: 200px; */
      height: 200px;
      background-color: pink;
    }

    .left {
      /* 左浮动 */
      float: left;
      margin-left: 20px;
    }

    .right {
      /* 右侧浮动 */
      float: left;
      height: 300px;
      background-color: purple;
    }

    .bottom {
      height: 50px;
      background-color: black;
    }
  </style>
</head>

<body>
  <div class="left">左侧123</div>
  <div class="right">右侧123</div>
  <div class="bottom"></div>
</body>

</html>

特点:浮动后的盒子顶对齐;具备行内块特点,可以设宽高;浮动后的盒子脱标,不占标准流的位置。浮动顶对齐与脱标效果图

易错点:示例里 .right 实际写的是 float: left,所以两个盒子都左浮动;如果要右对齐,应写 float: right。另外,浮动后脱标,父级若没有高度,可能撑不开父级。

产品区域左右布局 ​

为什么需要:产品区域常见“左侧菜单 + 右侧内容”的结构。怎么想:外层一个大盒子,里面放左右两个盒子,分别浮动到左右两侧。怎么做:HTML 结构如下:

html
  <div class="mi">
    <div class="left">左侧</div>
    <div class="right">右侧</div>
  </div>

CSS 样式:

html
 <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    li {
      list-style: none;
    }

    .mi {
      width: 1226px;
      height: 614px;
      /* background-color: pink; */
      margin: 100px auto;
    }

    .left {
      float: left;
      width: 234px;
      height: 614px;
      background-color: skyblue;
    }

    .right {
      float: right;
      width: 978px;
      height: 614px;
      background-color: purple;
    }
</style>

效果:左右浮动布局效果图

易错点:左侧 float: left,右侧要 float: right;父容器总宽度要能容纳左右宽度,否则右侧会掉下去。

区域小li布局 ​

为什么需要:右侧区域要放 8 个小商品块,并排成四列两行。怎么想:先让 li 左浮动,再给小 li 设宽高和间距。推导时注意:右侧宽 978px,小 li 宽 234px,间距 14px。四个 li 加三个间距是 234×4 + 14×3 = 978;如果每个 li 都加右外边距,就变成 234×4 + 14×4 = 992,超过 978,一行装不下四个,所以第 4、8 个要去掉右外边距。完整写法:

html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    li {
      list-style: none;
    }

    .mi {
      width: 1226px;
      height: 614px;
      /* background-color: pink; */
      margin: 100px auto;
    }

    .left {
      float: left;
      width: 234px;
      height: 614px;
      background-color: skyblue;
    }

    .right {
      float: right;
      width: 978px;
      height: 614px;
      /* background-color: purple; */
    }

    .right li {
      float: left;
      width: 234px;
      height: 300px;
      background-color: pink;
      margin-bottom: 14px;
      margin-right: 14px;
    }

    /* 让第4,8 个li 去掉外边距 */
    .right li:nth-child(4n) {
      margin-right: 0;
    }
  </style>
</head>

<body>
  <div class="mi">
    <div class="left">左侧</div>
    <div class="right">
      <ul>
        <li>1</li>
        <li>1</li>
        <li>1</li>
        <li>1</li>
        <li>1</li>
        <li>1</li>
        <li>1</li>
        <li>1</li>
      </ul>
    </div>
  </div>
</body>

</html>

小li四列布局效果图

易错点:选择器要限定为 .right li,不要写成全局 li;nth-child(4n) 用来选第 4、8 个;宽度和间距差一像素都可能导致换行。

清除浮动:问题从哪来 ​

为什么需要:浮动元素脱标,不占标准流位置;如果父级没有高度,子级无法撑开父级高度,下面的盒子就会升上来。怎么想:先搭出父级无高度、子级左右浮动的场景,再观察 .bottom 上移。场景代码:

html
<style>
  .top {
    margin: 10px auto;
    width: 1200px;
    /* height: 300px; */
    background-color: pink;
  }

  .left {
    float: left;
    width: 200px;
    height: 300px;
    background-color: skyblue;
  }

  .right {
    float: right;
    width: 950px;
    height: 300px;
    background-color: orange;
  }

  .bottom {
    height: 100px;
    background-color: brown;
  }

</style>

<div class="top">
  <div class="left"></div>
  <div class="right"></div>
</div>
<div class="bottom"></div>

父级无高度时底部盒子上移图

易错点:清除浮动不是“清除浮动元素”,而是清除浮动带来的高度塌陷影响;父级能直接给高度时问题会消失,但很多场景父级不方便给固定高度。

清除浮动的四种写法 ​

额外标签法:在父元素内容的最后添加一个块级元素,设置 clear: both。

html
<style>
.clearfix {
  clear: both;
}
</style>

<div class="father">
  <div class="left"></div>
  <div class="right"></div>
  <div class="clearfix"></div>
</div>

易错点:必须是块级元素;clear: both 清除左右浮动影响。

单伪元素法:用 ::after 在父元素最后生成一个块级盒子。

css
.clearfix::after {
  content: "";
  display: block;
  clear: both;
}
html
<div class="father clearfix"></div>

易错点:content、display: block、clear: both 三句都要有,缺一不可。

双伪元素法:before 解决外边距塌陷,after 清除浮动。

css
/* before 解决外边距塌陷问题 */
/* 双伪元素法 */
.clearfix::before,
.clearfix::after {
  content: "";
  display: table;
}

/* after 清除浮动 */
.clearfix::after {
  clear: both;
}
html
<div class="father clearfix"></div>

易错点:before 和 after 都要写;display: table 与 clear: both 分工不同。

overflow法:直接给父元素加 overflow: hidden。

css
.father {
  margin: 10px auto;
  width: 1200px;
  /* height: 300px; */
  background-color: pink;

  overflow: hidden;
}

易错点:overflow: hidden 会裁剪超出父盒子的内容;父级有固定高度时,孩子超出会被隐藏,但不会把下面盒子挤下去。

本节图示 ​

02-浮动(了解即可,不用复习) 图示 1

Flex布局是什么 ​

Flex 布局也叫弹性布局,是浏览器提倡的布局模型,非常适合结构化布局,提供了强大的空间分布和对齐能力。转写稿里把 flex 说成“flash”,是同音口误,实际属性是 display: flex。Flex 模型不会产生浮动布局中脱标现象,布局网页更简单、更灵活。转写中用小米右侧 8 个小盒子演示过:浮动后子级脱标,父级若没有高度就撑不开,底部盒子会升上来;而 Flex 不脱标,所以不用再走“清除浮动”那一套。

Flex布局示意

为什么需要:以前想把多个块级盒子放一行,常用浮动,但浮动会脱标,父级高度可能塌陷。
怎么想:Flex 的核心思路是“父级控制子级”,必须先有弹性容器。
怎么做:给父元素设置 display: flex,子元素可以自动挤压或拉伸。
易错点:不是给子元素加 display: flex 来控制自己排列;使用 Flex 排列的前提是有父容器。

Flex组成 ​

设置方式:给父元素设置 display: flex,子元素可以自动挤压或拉伸。组成部分包括:弹性容器、弹性盒子、主轴(默认在水平方向)、侧轴 / 交叉轴(默认在垂直方向)。

Flex组成示意

为什么需要:不理解“容器、盒子、主轴、侧轴”,后面 justify-content 和 align-items 很容易写反。
怎么想:父盒子叫弹性容器,子盒子叫弹性盒子;默认主轴水平,侧轴垂直。
怎么做:先写父级 display: flex,再分别记:主轴对齐用 justify-content,侧轴对齐用 align-items。
易错点:默认主轴是水平方向,不代表所有场景永远水平;本节先按默认轴理解。转写中反复强调“通过父亲控制孩子”,实际就是父级控制子级。

主轴对齐方式 ​

属性名:justify-content。

主轴对齐方式

重点记住标红的。如果非要问哪个常用,是 space-between。

css
justify-content: space-between;

显示效果:两侧没缝隙。

space-between效果

css
justify-content: space-around;

效果如下:记住2倍。

space-around效果

css
justify-content: space-evenly;

效果如下:记住空隙一样大。

space-evenly效果

css
justify-content: center;

效果如下:经常用于让一个盒子水平居中。

center效果

记忆:两侧没缝隙是 between;缝隙一样大是 evenly;2倍缝隙是 around。转写中演示时,先给父级加 display: flex,三个子盒子立刻一行;再切换 justify-content 观察分布变化。小米这类两侧贴边、中间留缝的结构,常用 space-between。

为什么需要:只写 display: flex 时子项默认挤在主轴起始位置,页面不一定好看。
怎么想:把剩余空间怎么分——贴两边用 between,均匀空隙用 evenly,盒子两侧都有空隙且中间约为两边两倍用 around,整体居中用 center。
怎么做:在弹性容器上写 justify-content。
易错点:space-between 是两侧没缝隙;space-around 不是完全等距,而是每个盒子左右都有相等空隙,视觉上中间约为两边两倍;space-evenly 才是所有空隙一样大。

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .box {
      /* 给父亲添加 flex */
      display: flex;
      /* 主轴的排列方式 */
      /* justify-content: flex-start; */
      /* justify-content: flex-end; */
      /* 让子盒子居中对齐 */
      /* justify-content: center; */
      /* between  */
      /* justify-content: space-between; */
      /* justify-content: space-around; */
      justify-content: space-evenly;
      width: 900px;
      height: 200px;
      background-color: pink;
    }

    .box div {
      width: 249px;
      height: 200px;
      background-color: skyblue;
    }
  </style>
</head>
<body>
  <div class="box">
    <div>1</div>
    <div>2</div>
    <div>3</div>
  </div>
</body>
</html>

主轴对齐运行效果

侧轴对齐方式 ​

  • align-items:当前弹性容器内所有弹性盒子的侧轴对齐方式(给弹性容器设置)
  • align-self:单独控制某个弹性盒子的侧轴对齐方式(给弹性盒子设置)

侧轴对齐方式

为什么需要:主轴只管一行里的左右分布,盒子在上下方向的对齐要靠侧轴属性。
怎么想:统一控制所有子项用 align-items;只改某一个子项用 align-self。
怎么做:在父级写 align-items: flex-start / flex-end / center / stretch。
易错点:stretch 是默认值,子盒子不写高度时会拉伸到与父级一样高;转写中演示过,父级有高度时子项可被拉伸,父级没高度时也会受内容影响。align-self 不要写到父级上。

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .box {
      /* 给父亲添加 flex */
      display: flex;
      /* 主轴的排列方式 */
      /* justify-content: flex-start; */
      /* justify-content: flex-end; */
      /* 让子盒子居中对齐 */
      /* justify-content: center; */
      /* between  */
      /* 两侧没缝隙 */
      /* justify-content: space-between; */
      /* 两倍缝隙 */
      /* justify-content: space-around; */
      justify-content: space-evenly;

      /* 侧轴的对齐方式 */
      /* 顶部对齐 */
      /* align-items: flex-start; */
      /* 底部对齐 */
      /* align-items: flex-end; */
      /* 居中对齐 */
      /* align-items: center; */
      /* 默认的 拉伸和父亲一样 */
      align-items: stretch;
      width: 900px;
      height: 500px;
      background-color: pink;
    }

    .box div {
      width: 249px;
      /* height: 200px; */
      background-color: skyblue;
    }
  </style>
</head>
<body>
  <div class="box">
    <div>1</div>
    <div>2</div>
    <div>3</div>
  </div>
</body>
</html>

侧轴stretch效果

盒子水平和垂直居中的写法 ​

水平垂直居中示意

为什么需要:传统写法让一个盒子在父级中水平垂直居中比较绕,Flex 两行就能完成。
怎么想:默认主轴水平,所以 justify-content: center 管水平居中;侧轴垂直,所以 align-items: center 管垂直居中。
怎么做:父级写 display: flex; justify-content: center; align-items: center;。
易错点:两个属性都写在父级;不要把 justify-content 和 align-items 的作用轴记反。

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .father {
      /* 利用flex实现 */
      display: flex;
      /* 主轴水平居中 */
      justify-content: center;
      /* 侧轴水平居中 */
      align-items: center;
      width: 300px;
      height: 300px;
      background-color: pink;
    }

    .son {
      width: 200px;
      height: 200px;
      background-color: purple;
    }
  </style>
</head>
<body>
  <div class="father">
    <div class="son"></div>
  </div>
</body>
</html>

代码示例 ​

示例 1:浮动基本使用 ​

html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>浮动</title>
  <style>
    .left,
    .right {
      /* width: 200px; */
      height: 200px;
      background-color: pink;
    }

    .left {
      /* 左浮动 */
      float: left;
      margin-left: 20px;
    }

    .right {
      /* 左侧浮动 */
      float: left;
      height: 300px;
      background-color: purple;
    }

    .bottom {
      height: 50px;
      background-color: black;
    }
  </style>
</head>

<body>
  <div class="left">左侧123</div>
  <div class="right">右侧123</div>
  <div class="bottom"></div>
</body>

</html>

要点解释

  • float: left 让块元素水平排列,取值只有 left / right,没有 center,也不存在“居中浮动”。
  • 浮动后的盒子顶对齐、具备行内块特点(可给宽高、不换行)、并且脱标,不占用标准流位置。
  • 这里 .left / .right 的 width 被注释掉了,宽度由内容撑开;把注释打开才固定为 200px。
  • 易错点:.bottom 是标准流盒子,会钻到浮动盒子下面(被 .right 的 300px 高度盖住),这就是后面要“清除浮动”的原因;另外父级没有高度时,浮动的子级无法撑开父级高度。

示例 2:产品区域(小米)左右布局 + 小 li 布局 ​

html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    li {
      list-style: none;
    }

    .mi {
      width: 1226px;
      height: 614px;
      /* background-color: pink; */
      margin: 100px auto;
    }

    .left {
      float: left;
      width: 234px;
      height: 614px;
      background-color: skyblue;
    }

    .right {
      float: right;
      width: 978px;
      height: 614px;
      /* background-color: purple; */
    }

    .right li {
      float: left;
      width: 234px;
      height: 300px;
      background-color: pink;
      margin-bottom: 14px;
      margin-right: 14px;
    }

    /* 让第4,8 个li 去掉外边距 */
    .right li:nth-child(4n) {
      margin-right: 0;
    }
  </style>
</head>

<body>
  <div class="mi">
    <div class="left">左侧</div>
    <div class="right">
      <ul>
        <li>1</li>
        <li>1</li>
        <li>1</li>
        <li>1</li>
        <li>1</li>
        <li>1</li>
        <li>1</li>
        <li>1</li>
      </ul>
    </div>
  </div>
</body>

</html>

要点解释

  • 大盒子 .mi 宽 1226px,用 margin: 100px auto 水平居中;左侧 234px 左浮动,右侧 978px 右浮动,正好铺满。
  • 结构位置不能写错:8 个 li 是放在 .right 里面的 ul 中,选择器写 .right li 才生效。
  • 关键算式(易错点):234 + 14 = 248,248 × 4 = 992 > 978,所以一行放不下 4 个,会掉行。必须用 :nth-child(4n) 把第 4、8 个 li 的右外边距强制改为 0,这样 234×4 + 14×3 = 978 才能正好一行 4 个,一像素都不能多、不能少。
  • margin-bottom: 14px 全部保留(第二排下方的间距不影响布局);如果觉得底部多出 14px 别扭,可以再给父级加 overflow: hidden 切掉。

示例 3:Flex 主轴对齐 + 侧轴对齐 ​

html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .box {
      /* 给父亲添加 flex */
      display: flex;
      /* 主轴的排列方式 */
      /* justify-content: flex-start; */
      /* justify-content: flex-end; */
      /* 让子盒子居中对齐 */
      /* justify-content: center; */
      /* 两侧没缝隙 */
      /* justify-content: space-between; */
      /* 两倍缝隙 */
      /* justify-content: space-around; */
      justify-content: space-evenly;

      /* 侧轴的对齐方式 */
      /* 顶部对齐 */
      /* align-items: flex-start; */
      /* 底部对齐 */
      /* align-items: flex-end; */
      /* 居中对齐 */
      /* align-items: center; */
      /* 默认的 拉伸和父亲一样 */
      align-items: stretch;
      width: 900px;
      height: 500px;
      background-color: pink;
    }

    .box div {
      width: 249px;
      /* height: 200px; */
      background-color: skyblue;
    }
  </style>
</head>

<body>
  <div class="box">
    <div>1</div>
    <div>2</div>
    <div>3</div>
  </div>
</body>

</html>

要点解释

  • display: flex 必须写在父级(弹性容器)上,子元素(弹性盒子)才会自动在一行上挤压/拉伸;写在子元素上无效。
  • justify-content 控制主轴(默认水平)对齐:flex-start / flex-end / center / space-between(两侧没缝隙,最常用)/ space-around(两侧缝隙是中间的 2 倍)/ space-evenly(缝隙一样大)。
  • align-items 控制侧轴(默认垂直)对齐,作用于容器内所有弹性盒子;单个盒子可用 align-self 单独设置。
  • 易错点:.box div 的 height 被注释掉后,默认 align-items: stretch 会让子盒子自动拉伸到和父亲一样高,所以看不到 200px 的高度效果;把 height: 200px 打开或改成 align-items: center 才能看出差别。
  • 补充(笔记同款写法):盒子水平垂直居中只需在父级写 display: flex; justify-content: center; align-items: center;;Flex 布局不脱标,不会出现浮动那种父级高度塌陷,也就无需清除浮动。

常见错误 ​

  1. 现象:父级没有高度,子元素浮动后父级“塌陷”,底部盒子升上来。
    原因:浮动元素脱标,不占标准流位置,无法撑开父级高度。
    修法:给父级清除浮动(双伪元素 clearfix / overflow:hidden / 额外标签法),或改用 Flex 布局。

  2. 现象:一行本应放 4 个商品卡,结果只放 3 个或最后一个掉行。
    原因:子项宽度 + margin 总和超过父容器宽度,例如 234×4 + 14×4 > 978。
    修法:去掉每行最后一个的右 margin,如 .right li:nth-child(4n){margin-right:0};更推荐 Flex + gap。

  3. 现象:给子元素写 display:flex,布局没有按预期横向排列。
    原因:Flex 是“父控子”,display:flex 应写在弹性容器(父级)上。
    修法:把 display:flex 写到父容器,子项自动成为弹性盒子。

  4. 现象:想实现盒子水平垂直居中,只写了一个对齐属性,结果只居中一个方向。
    原因:主轴对齐和侧轴对齐分工不同,且必须作用于弹性容器。
    修法:父级同时写 display:flex; justify-content:center; align-items:center;。

  5. 现象:写了 ::after{clear:both} 但清除浮动无效。
    原因:伪元素缺少 content,或仍是行内元素,未形成块级盒子。
    修法:使用完整写法:content:""; display:block; clear:both;。

练习 ​

  1. 用 Flex 实现:父容器内 3 个盒子横向排列,两端贴边、中间间距均分,且所有盒子在父容器内垂直居中。写出父级关键 CSS。

  2. 用浮动完成 2 行 4 列商品卡布局:每个卡片宽 234px、高 300px,右间距和下间距均为 14px,要求每行第 4 个卡片没有右间距。写出关键选择器和清除浮动方案。

  3. 给定结构:父级无高度,内部两个浮动子元素,下面还有一个底部盒子。分别用“双伪元素法”和“overflow 法”修复布局,并说明两种方案的使用差异。

  4. 父容器内有 3 个子项,分别设置 justify-content: space-between / space-around / space-evenly,画出或描述三种间距关系,并写出你的记忆规则。

  5. 把第 2 题的商品卡浮动布局改造成 Flex 布局,要求实现同样的 4 列排列和 14px 间距,并说明改造后为什么不再需要清除浮动。

原视频对照 ​

#视频时长
1小米模块布局下09:04
2小问题04:19
3清除浮动21:37
4浮动的本质04:05
5flex布局认识04:27
6flex布局体验02:05
7主轴的排列方式17:42

小结 ​

  1. 浮动只需了解,现代布局优先使用 Flex。
  2. 浮动脱标是父级塌陷、底部盒子上移的根源。
  3. 清除浮动清除的是“浮动带来的布局影响”,不是取消浮动本身。
  4. Flex 的核心是父容器控制子项,display:flex 写在父级。
  5. 主轴排列看 justify-content,侧轴对齐看 align-items / align-self。
  6. space-between 最常用;space-around 记两倍关系;space-evenly 记间距均等。
  7. 商品卡多列布局优先用 Flex + gap,比浮动逐项处理 margin 更稳。
  8. clearfix 必须满足:生成内容、块级显示、clear:both。