CSS - 布局 - 弹性盒子

1,213 阅读10分钟

Flexbox 布局是一种用于按行或按列布局元素的一维布局方法,可以使元素自动调整大小以适应可用空间

Flexbox布局允许元素膨胀以填充额外的空间,也可以收缩以适应更小的空间

基本构成

flex布局主要由以下两个部分组成

分类说明
flex container开启了 flex 布局的那个元素 --- 父容器
flex itemflex container 里面的直接子元素 --- 容器中的元素

Ljnl3X.png

Flex item 的特点:

  1. flex item的布局将根据flex container属性值来进行布局
  2. flex item不再严格区分块级元素和行内级元素
  3. flex item默认情况下是包裹内容的, 但是可以设置宽度和高度

开启flex布局

我们可以设置display属性的值为flex或者inline-flex来为当前元素开启弹性布局

flex类型说明
flexflex container 以 block-level 形式存在
inline-flexflex container 以inline-level形式存在

盒子模型

flex item会沿着主轴(从 main-start 到 main-end)或横轴(从 cross-start 到 cross-end)排列

Ii40Ue.png

container相关属性

flex-direction

flex items 默认都是沿着 main axis(主轴)从 main start 开始往 main end 方向排布

flex-direction 决定了 main axis 的方向,因此我们可以使用flex-direction来改变元素的排列方向

说明
row元素从左往右排列,默认值
row-reverse元素从右往左排列
column元素从上往下排列
column-reverse元素从下往上排列

LjnY1L.png

flex-wrap

flex-wrap 决定了 flex container 是单行还是多行

说明
nowrap单行显示,不换行 --- 默认值
wrap多行显示
wrap-reverse多行
对比 wrap,cross start 与 cross end 相反

Code Snippet of Day 10: flex-wrap

在默认情况下,Flexbox 布局的 flex-wrap 属性值为 nowrap,这意味着它不会换行。当一行中的元素放不下时,Flexbox 布局会尽可能地压缩元素的宽度或高度来适应可用空间

因此,即使为 Flexbox 布局中的元素显示设置了对应的宽度和高度,元素最终展示出来的宽度或高度可能与所设置的宽度和高度不同。元素的显示设置的宽度和高度只能说存在关系,但不是必然的结果

flex-flow

flex-flow 属性是 flex-direction 和 flex-wrap 的简写,并且顺序任何, 并且都可以省略

<flex-direction> || <flex-wrap>

justify-content

justify-content 决定了flex items在 main axis上的对齐方式

说明示意图
flex-start与 main start 对齐
默认值
Lj7qEz.png
flex-end与 main end 对齐Lj7Wju.png
center居中对齐Lj755G.png
space-between与 main start、main end两端对齐
flex items 之间的距离相等
Lj77t4.png
space-aroundflex items 与 main start、main end 之间的距离是 flex items 之间距离的一半
flex items 之间的距离相等
Lj7Q0X.png
space-evenlyflex items 与 main start、main end 之间的距离 等于 flex items 之间的距离
flex items 之间的距离相等
Lj7eID.png

align-items

align-items 决定了 flex items在cross axis上的对齐方式

说明示意图
normal在弹性布局中,效果和stretch一样
默认值
Lj794p.png
strech当 flex items 在 cross axis 方向的 size 为 auto 时
会 自动拉伸至填充 flex container
Lj794p.png
flex-start与 cross start 对齐Lj7TM6.png
flex-end与 cross end 对齐Lj7iVT.png
center居中对齐Lj7xkE.png
baseline与基准线对齐Lj7D9Q.png

align-content

align-content 决定了多行flex items在cross axis上的对齐方式,用法与 justify-content 类似

说明示意图
stretch与 align-items 的 stretch 类似
默认值
LjQeSi.png
flex-start与 cross start 对齐LjQl3k.png
flex-end与 cross end 对齐LjQVRe.png
center居中对齐LjQ8uy.png
space-between与justify-content的space-between效果一致LjQABr.png
space-around与justify-content的space-around效果一致LjQXO5.png
space-evenly与justify-content的space-evenly效果一致LjQHQz.png

align-content使用的先决条件

  1. 必须有多行align-items
  2. 必须存在剩余空间,可以给flex container划分空间

所以align-content在实际开发中很少使用,因为在实际开发中,在绝大多数情况下,flex-container的高度是由flex-items中的内容撑开的

这也就意味着,没有剩余的空间给align-content进行分配, 此时设置或者不设置align-content属性,效果都是一致的

item相关属性

order

order 决定了 flex items 的排布顺序

  • 可以设置任意整数 (正整数、负整数、0)
  • 值越小就越排在前面,
  • 如果值相同,则值相同的元素按照默认先后顺序排列
  • 默认值是 0

LjQoCS.png

align-self

flex items 可以通过 align-self 覆盖 flex container 设置的 align-items

  • auto(默认值): 遵从 flex container 的 align-items 设置
  • stretch、flex-start、flex-end、center、baseline,效果跟 align-items 一致

LjQBqW.png

flex-grow

flex-grow 决定了 flex items 如何扩展 (拉伸/成长)

  • 可以设置任意非负数字(正小数、正整数、0),默认值是 0
  • 当 flex container 在 main axis 方向上有剩余 size 时,flex-grow 属性才会有效
  • flex items 扩展后的最终 size 不能超过 max-width/max-height

flex-grow拉伸的计算方式如下:

flex items 的 flex-grow 总和 sum每个 flex item 扩展后的 size
大于1flex container 的剩余size * flex-grow / sum
小于1flex container 的剩余 size * flex-grow

LjQify.png

flex-shrink

flex-shrink 决定了 flex items 如何收缩(缩小)

  • 可以设置任意非负数字(正小数、正整数、0),默认值是 1

  • 当 flex items 在 main axis 方向上超过了 flex container 的 size,flex-shrink 属性才会有效, 也就是元素溢出了,flex-shrink才会生效

  • flex items 收缩后的最终 size 不能小于 min-width/min-height

  • 如果没有设置最小高度或最小高度,则不小于flex-item中内容的宽度或高度

flex-shrink的计算方式如下:

  • 如果flex-shrink总和大于1,则:

    1. 计算总的缩放宽度:total shrink scaled width=∑(width×flex-shrink)

    2. 计算每个元素的缩放比例:shrink ratio=width×flex-shrink / total shrink scaled width

    3. 计算新的宽度:new width=width−(shrunk space×shrink ratio)

      image.png

  • 如果flex-shrink总和小于1,则原width - 原宽度 * flex-shrink

flex-basis

flex-basis 属性用于设置 Flexbox 布局中flex-item在主轴上的初始大小, flex-basis的优先级高于width/height

flex-basis 属性只会影响flex-item在 Flexbox 布局的初始大小,flex-item的后续大小取决于他们在flex-cotainer中是如何布局的

换句话说widthheight 属性在设置后值基本不会发生改变,但是flex-basis的值可能会根据flex布局而重新调整

需要注意的是,如果flex-basiswidth/height同时存在,那么该flex-item可以设置的最大宽度或高度为width/height

flex-basis可以取的值:

  • auto (默认值)
    • 如果没有设置width,那么默认值表现为内容的宽度
    • 如果设置width,那么默认值表现为width
  • 具体的宽度数值(100px)
  • 可以是一个百分比
    • 该百分比相对的是其父弹性盒容器主轴尺寸

决定 flex items 最终 base size 的因素,从优先级高到低:

  1. max-width / max-height / min-width / min-height
  2. flex-basis
  3. width / height
  4. 内容本身的 size

flex

flex 是 <flex-grow> <flex-shirink>? || <flex-basis> 的简写

flex 属性可以指定1个,2个或3个值

# flex: none --> flex: 0 0 auto;
# flex: auto --> flex: 1 1 auto;
# flex: initial --> flex: 0 1 auto; ---> 恢复为默认初始值
none | auto | initial | <flex-grow> <flex-shirink>? || <flex-basis>

单值语法: 值必须为以下其中之一:

  • 一个无单位数(<number>): 它会被当作<flex-grow>的值
  • 一个有效的宽度(width)值: 它会被当作 <flex-basis>的值
  • 关键字none,auto

**双值语法: **

第一个值必须为一个无单位数,并且它会被当作 <flex-grow> 的值

第二个值必须为以下之一:

  • 一个无单位数:它会被当作 <flex-shrink> 的值
  • 一个有效的宽度值: 它会被当作<flex-basis> 的值

三值语法:

  • 第一个值必须为一个无单位数,并且它会被当作 <flex-grow> 的值
  • 第二个值必须为一个无单位数,并且它会被当作 <flex-shrink>的值
  • 第三个值必须为一个有效的宽度值, 并且它会被当作 <flex-basis> 的值

flex布局应用

flex最后一行对齐问题

LjeJL4.png

在绝大多数情况下,我们希望前面几行是按照我们所需要的布局进行排列,而最后一行是从左往右依次排列

例如; 在上述图中,我们希望第一行和第二行实现space-between, 而最后一行实现从左往右依次进行排列

也就是实现如下图效果

Lje12G.png

解决方法一

通过计算,手动去设置对应的margin来实现排列效果,而不是使用justify-content属性

不推荐,因为如果元素个数 或 宽度值(flex-item的或flex-container的)发生改变后,需要重新计算,扩展性不强

<!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>
    .container {
      width: 600px;
      background-color: gray;
      display: flex;
      flex-wrap: wrap;
      /*
        通过手动计算margin值来实现布局
        所以在这样不需要再通过justify-content来进行布局
      */
      /* justify-content: space-between; */
    }

    .item {
      width: 160px;
      height: 160px;
      color: #fff;
      font-size: 20px;
      line-height: 160px;
      text-align: center;
      margin-bottom: 20px;
      /*
        margin-right = (600 - 160 * 3)/ 2
      */
      margin-right: 60px;
    }

    /* 最右边那一列元素移除margin-right的值 */
    .item:nth-child(3n) {
      margin-right: 0;
    }

    /* 最后3个节点 移除下外边距 */
    .item:nth-last-child(-n + 3) {
      margin-bottom: 0;
    }
  </style>
</head>
<body>
  <div class="container">
    <div class="item item1">item1</div>
    <div class="item item2">item2</div>
    <div class="item item3">item3</div>
    <div class="item item4">item4</div>
    <div class="item item5">item5</div>
    <div class="item item6">item6</div>
    <div class="item item7">item7</div>
    <div class="item item8">item8</div>
  </div>

  <!-- 该脚本用于生成div.item的随机色 -->
  <script src="./index.js" ></script>
</body>
</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>
    .container {
      width: 600px;
      background-color: gray;
      display: flex;
      flex-wrap: wrap;
      justify-content: space-between;
    }

    .item {
      width: 160px;
      height: 160px;
      color: #fff;
      font-size: 20px;
      line-height: 160px;
      text-align: center;
      margin-bottom: 20px;
    }

    span {
      /*
        只要保证span元素的宽度 === div.item的宽度即可
        因为span的高度默认的0
        所以理论上设置任意个数的span都不会影响flex布局的最终显示效果
        但是一般最少设置的个数为 列数 - 2
      */
      width: 160px;
    }

    /*
    	本例中显示的是三例
    	所以需要至少添加一个占位元素
    	那么就意味着在本案例中需要为最后4个元素清除下外边距
    */
    :nth-last-child(-n + 4) {
      margin-bottom: 0;
    }
  </style>
</head>
<body>
  <div class="container">
    <div class="item item1">item1</div>
    <div class="item item2">item2</div>
    <div class="item item3">item3</div>
    <div class="item item4">item4</div>
    <div class="item item5">item5</div>
    <div class="item item6">item6</div>
    <div class="item item7">item7</div>
    <div class="item item8">item8</div>

    <!-- 
	1. 需要填充的元素的个数是列数-2 
	2. span在这里的作用仅仅只是为了做布局的额外填充元素
	      所以在这里可以使用任何元素来作为填充元素,不一定是span元素
	      推荐使用i元素,因为i元素只有一个字符,所占字符最小
     -->
    <span></span>
  </div>

  <!-- 该脚本用于生成div.item的随机色 -->
  <script src="./index.js" ></script>
</body>
</html>

flex属性结合溢出隐藏

LTRfo6.png

<!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>
    .container {
      width: 300px;
      background-color: gray;
      display: flex;
      margin: 100px auto;
    }

    .center {
      /* 
      	div.center的宽度自动占满剩余空间
      	相当于视情况 动态设置div.center的宽度
      	所以在这里也可以使用溢出省略号的效果
      */
      flex: 1;
      text-overflow: ellipsis;
      overflow: hidden;
      white-space: nowrap;
      background-color: skyblue;
    }
  </style>
</head>
<body>
  <div class="container">
    <div class="left">left part</div>
    <div class="center">Lorem ipsum dolor sit amet consectetur adipisicing elit. Fugit, nisi.</div>
    <div class="right">right part</div>
  </div>
</body>
</html>

flex: 1 和 text-overflow: ellipsis冲突问题

<!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>
    .container {
      width: 600px;
      display: flex;
      list-style: none;
    }

    .item {
      height: 100px;
      background-color: skyblue;
      border: 1px solid gray;
    }

    .item1,
    .item3 {
      width: 100px;
    }

    .item2 {
      flex: 1;
      /* min-width: 0; */
      overflow: hidden;
    }

    span {
      text-overflow: ellipsis;
      white-space: nowrap;
      overflow: hidden;
      display: block;
    }
  </style>
</head>
<body>
  <ul class="container">
    <li class="item item1">lorem</li>
    <!--
      如果一个元素的父元素设置了flex: 1;
      而该元素上设置了text-overflow: ellipsis;
      那么flex:1 就会和 text-overflow: ellipsis; 产生冲突

      原因:
        父元素flex:1; 子元素 text-overflow: ellipsis;的时候
        浏览器会认为 当前元素的宽度值是auto; 是可以被撑开的
        因此此时子元素就没有了width的限制,所以text-overflow: ellipsis;无效

      解决方法:
        1. 子元素必须是块级元素(如果子元素不是块级元素,需要先转换为块级元素)
        2, 父元素设置`min-width: 0;`或`overflow`不为`visible`的值(一般设置为`none`或`auto`)
     -->
    <li class="item item2">
      <span>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Aut maxime facere voluptate fugiat adipisci sunt ipsam reprehenderit ullam ut et!</span>
    </li>
    <li class="item item3">lorem</li>
  </ul>
</body>
</html>