flex也存在grid同款网格?——从flex和gird的通用属性看其实际意义

189 阅读6分钟

主题

众所周知,align-items等属性可以在flex布局和grid布局中同时发挥作用

本篇讲的是flexgrid布局以下部分通用属性在各自布局中的含义:

  1. justify-content
  2. justify-items
  3. justify-self
  4. align-content
  5. align-items
  6. align-self

引起思考缘由

对于以下代码:

<div class="container">
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
</div>
<style>
    .container {
        width: 300px;
        height: 400px;
        border:1px solid red;
        display: flex;
        flex-direction: row;
        justify-content: space-around;
        flex-wrap: wrap;
    }
    .child {
        height: 100px;
        width: 100px;
        border: 1px solid blue;
    }

image.png

我们会发现一个问题:为什么flex换行后的第二行内容距离第一行这么远?为什么不是紧挨着第一行?

假如此时为容器加上align-items:center:

image.png

嗯。。。好像更奇怪了,难道他们不应该是都集中在最中间吗?

但是假如此时你正好打开了firefoxdevtools!并且恰巧打开了这个选项:

image.png

就会发现:

image.png

flex布局中竟然出现了网格?这和下面的grid布局非常相像

<div class="container">
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
</div>
<style>
    .container {
        width: 300px;
        height: 400px;
        border:1px solid red;
        display: grid;
        grid-template-columns: 100px 100px;
        justify-content: space-around;
        align-items: center;
    }
    .child {
        height: 100px;
        width: 100px;
        border: 1px solid blue;
    }
</style>

image.png

由此引起我的好奇:

难道flex布局也存在网格?其与gird布局有什么共通之处呢?这会不会与其通用属性有关呢?

Flex布局也存在网格

观察以上代码,不难发现:

当发生换行时,在flex布局中的align-items:center的所谓的居中就像是在平分容器高度的网格中居中一样

MDN官方称flex一维布局,grid布局为二维布局,从这一论调中可以看出其实两者关系是相当密切的,再加上其草案提出以及正式成为标准的时间比较接近,可以合理认为在底层实现上也许是存在一定联系和复用的,比如flex也存在网格

-content-items-self分别代表什么?

  1. content:需要为flex或者grid容器设置,代表网格容器中的分布规则
  2. items: 需要为flex或者grid容器设置,代表实际的元素网格中的分布规则
  3. self: 需要为flex或者grid子项设置,代表该元素网格中的分布规则

下面就借助firefox devtools实时展示出表格证明

content

flexgrid都可以生效的align-content为例观察

首先看看MDN关于align-content的描述:

CSS 的 align-content 属性设置了浏览器如何沿着弹性盒子布局的纵轴和网格布局的主轴在内容项之间和周围分配空间。

好像看不太懂。。那就再看看W3C的flex标准CSS Flexible Box Layout Module Level 1是如何定义align-content的:

The align-content property aligns a flex container’s lines within the flex container when there is extra space in the cross-axis, similar to how justify-content aligns individual items within the main-axis. Note, this property has no effect on a single-line flex container.

这里也并不像grid规范一样说明了align-content用于规范网格的分布,因此还是尝试一下好

回顾刚才的flex发生换行时的纵向排布问题,分别为align-content取不同的值:

首先是默认值stretch

image.png

情况不出所料,区域被拉长到填满容器,因为发生了换行,所以两行的网格平分了容器高度

centerstart又如何呢?

center

image.png

start

image.png

以上种种行为都和grid-template-rows未指定时的grid布局相同。

观察后发现:align-contentflexgird中都像是分配网格分布,只不过在W3C对于flex的标准中,将这种情况描述为:当有空余空间(exrta space)时,该属性负责对齐(align)flex容器(flex container)的线(line),我们可以理解为如何分配flex中的空余空间,其实际表现和gird中网格分布相同,只不过flex不能显示指定网格大小,只能选择填满父元素还是和内容元素高度相同。

不生效的情况

W3C规范中提到:

Note, this property has no effect on a single-line flex container.

W3C对于single-line flex containermutil-line flex container的定义如下

  • A single-line flex container (i.e. one with flex-wrap: nowrap) lays out all of its children in a single line, even if that would cause its contents to overflow.
  • A multi-line flex container (i.e. one with flex-wrap: wrap or flex-wrap: wrap-reverse) breaks its flex items across multiple lines, similar to how text is broken onto a new line when it gets too wide to fit on the existing line. When additional lines are created, they are stacked in the flex container along the cross axis according to the flex-wrap property. Every line contains at least one flex item, unless the flex container itself is completely empty.

解释一下就是单行flex容器是flex-wrap:nowrap的容器,而多行flex容器则是flex-wrap:wrap或者flex-wrap:wrap-reverse,因此在单行flex容器中,该属性是无效的,其网格空间始终填满容器高度

items

items属性在grid中负责管理网格中的元素分配规则,在flex也是这样的

对于以上问题中align-content值为stretch的情况,align-items分别取start,center以及去掉除子元素高度后的strrtch:

image.png

image.png

image.png

不出所料,和grid中的align-items表现相同

self

上面几个属性搞清楚了,这个就很容易了

在MDN中,对align-items属性的描述:

CSS align-items 属性设置了所有直接子元素的 align-self 值作为一个组。在 Flexbox 中,它控制子元素在交叉轴上的对齐。在 Grid 布局中,它控制了子元素在其网格区域内的块向轴上的对齐。

align-self的描述:

CSS 属性 align-self 会对齐当前 grid 或 flex 行中的元素,并覆盖已有的 align-items 的值。In Grid, it aligns the item inside the grid area. 在 Flexbox 中,会按照 cross axis(当前 flex 元素排列方向的垂直方向)进行排列。

非常清晰,无需多言

如何将多行flex容器中的副轴元素集中到中间

如果使用flex布局不考虑align-content而只是考虑align-items,那这样的布局是难以实现的:

image.png

在知道这一切的原理后,只需要加上align-content:center

justify-contentflex横向上无效?

经过上面分析,可知content管网格分布(在flex则是类似grid的空间分布),而items则是在网格中的分布,而flex显然不能指定网格的宽度,因此可以看作是flex布局中横向网格中,所有的网格宽度和其元素宽度一致,所以此时是由justify-content决定分布,而由于网格宽度等于元素宽度,justify-itemsjustify-self无效

另外一个通用属性:gap

gaprow-gapcolumn-gap的缩写,需要为容器设置,描述了纵向和横向网格之间的间距

这个元素在flexgrid中都会生效:

  • grid布局中,其描述了row之间的间距和col之间的间距
  • flex布局中,其描述了:
    • 当发生换行时,各行之间的间距
    • 主轴元素之间的间距

gap起源于grid布局,最开始叫做grid-gap,后拓展到flex布局

flex布局中,gap也是指网格之间的距离,如下:

    <div class="father">
        <div class="child" ></div>
        <div class="child"></div>
        <div class="child"></div>
        <div class="child"></div>
    </div>
    <style>
        .child {
            width: 200px;
            height: 200px;
            border: 1px solid red
        }
        .father {
            display: flex;
            justify-content: start; 
            gap: 30px;
            width: 500px;
            height: 500px
            border: 1px solid green;
            flex-wrap: wrap;
            padding: 20px
        }
    </style>

image.png

这也可以佐证flex主轴元素的网格和这个元素尺寸相同这个观点