网络埋伏纪事

理解弹性盒:必知必会

网络埋伏纪事 · 2017-02-14翻译 · 2402阅读 原文链接

本文将涵盖玩转 CSS 弹性盒模型所需的所有基本概念。文章有点长,请做好准备。

如果你更愿意在一个 PDF 文档中阅读整个教程,这里是下载链接 - 随便下,没有附加条件。

关于弹性盒学习曲线的备注

如下是我很尊重的一个开发者 Philip Roberts 发的一个推文:

学习弹性盒最初可能不咋好玩,它会颠覆你所知道的 CSS 布局知识。不过没关系,所有值得学的东西都是以这样的方式开始。

弹性盒肯定是你应该认真对待的东西。它为内容布局的现代风格铺平了道路,而且也不会很快消失,因为现在它已经成为了一种新标准。所以就张开双臂,拥抱它吧!

你将学到什么

我会首先介绍弹性盒的基础知识。我认为要理解弹性盒必须从这里开始。

Flexbox fundamentals

学习基础知识很酷,不过更酷的是用这些基础知识创建实际应用。

我会带你创建很多小玩意儿。之后,会用这个完全用弹性盒布局的音乐应用将这些小玩意儿包起来。

Music app layout

看起来好看不?

在学习创建音乐应用布局的同时,我会深入研究弹性盒的内部工作机制。你还会切身体会一下弹性盒在响应式网页设计中扮演的角色。

我很兴奋向你展示这一切。

GIF by Jona Dinges

不过在着手创建用户界面之前,我打算先带你做一些练习。这看起来可能会比较枯燥,不过这就是玩转弹性盒的所有过程。

下面我们开始吧。

简介

在过去几年中,CSS 已经进化了很多。设计师喜欢 CSS 中新引入的过滤器、渐变、变形。但是有些东西还是缺失的,有些东西是我们都很渴望它有的。

用 CSS 精巧制作智能的页面布局貌似已经有太长时间了,而这让我们中许多人要编写旁门左道的 CSS。

我们总是不得不处理 float、table display hack,以及它们带来的影响。如果你已经写过一段时间的 CSS,对此可能有所体会。如果没有的话,那么欢迎你来到一个更好的世界!

貌似我们作为设计师和前端开发者的祈祷终于已经被听到了。这次,是以庄严的方式。

现在我们可以完全抛弃那些旁门左道的 CSS 招数了,不再总是用 float 和 table-cell display 了。

是时候拥抱创建智能布局的更干净的现代语法了。欢迎 CSS 弹性盒模型。

什么是弹性盒

根据规范,弹性盒(Flebox)模型旨在提供一种更有效的方式,为文档中的元素之间进行布局、对齐和分配空间 - 即便视口和元素的大小是动态变化的或者未知的,也能很好适应。

如果这听起来太正式,我理解这种感受。稍后,我会用浅显的话解释这段话的含义。不管你是从没玩过 CSS,还是刚刚开始,都会感到很舒服。

如何开始使用弹性盒模型

这是每个人会问的第一个问题,答案比你想像的要更简单一些。

要开始使用弹性盒模型,只需先定义一个弹性容器(Flex Container)。

在常规 HTML 中,采用如下形式创建一个简单的条目列表:

<ul> <!--父元素-->
  <li></li> <!--第一个子元素-->
  <li></li> <!--第二个子元素-->
  <li></li> <!--第三个子元素-->
</ul>

扫一眼,就会看到无序列表元素(ul)包含着列表项元素(li)。

你会称 ul 为父元素,li 为子元素。

要使用弹性盒模型,必须让父元素成为弹性容器(又称可伸缩容器)。

通过设置 display: flex 或为行内元素设置display: inline-flex,就可以搞定。很简单吧,从这里开始,所有设置就是使用弹性盒模型。

这两个设置实际上是立即触发了一个弹性盒格式化上下文。

我说过并没有想像的那么难吧。

对于使用无序列表和一堆列表项元素,如下是触发一个弹性盒格式化上下文的 CSS 代码。

/* 把父元素变成一个弹性容器*/
ul {
  display: flex; /*或者 inline-flex*/
}

下面给列表项加上点样式,这样就可以看到发生了什么。

li {
  width: 100px;
  height: 100px;
  background-color: #8cacea;
  margin: 8px;
}

你应该得到下面这样的东西:

Flexbox activated

你可能还没注意到,不过有些事情已经发生了。弹性盒格式化上下文现在已经被触发了。

还记得吧,默认情况下,CSS 中的 "div" 是像这样,从上到下垂直堆放的:

Default viewing for ‘divs’

上面的图像是你希望会得到的结果。

不过,包含了一行 display:flex,会马上看到布局上的变化。

列表项元素现在是从左到右,水平堆放,就像你用 float 实现的一样。

Flexbox enabled

一旦你在任何父元素上引入"弹性显示",弹性盒模型就立即生效。

可能你没法理解为什么会形成列表项元素方向上的这种变化。我承诺过很快会深入研究弹性盒的内部工作机制。暂时信我一回好了。

理解 "弹性显示" 的引入是弹性盒模型的开始。

还有点事情需要提醒你注意。

一旦将 display 属性设置为 flex,无序列表就会自动变成 弹性容器,子元素(即,列表项元素 li)就变成了 弹性项目

在我告诉你一些弹性盒模型具备的有趣事情时,这些术语会适时一次又一次出现。

我已经用了两个关键词,我想把重点放在它们身上。它们对理解会发生什么至关重要。

  1. 弹性容器(Flex Container) : 设置 display: flex 的父元素。

  2. 弹性项目(Flex Item) : 弹性容器内的子元素。

这是使用弹性盒模型的基石。

弹性容器的属性

Flex-direction || Flex-wrap || Flex-flow || Justify-content || Align-items || Align-content

在上面的小节中,我确定了一些基本原则。弹性容器及弹性项目是什么,以及如何触发弹性盒模型。

现在到了将所有这些加以利用的时候了。

将父元素设置为一个弹性容器后,就可以在弹性容器上用几个对齐属性了。

就像你会在块级元素上将 width 属性定义为 width: 200px 一样,弹性容器也有 6 个不同的属性。

好消息是定义这些属性的方式与以前你用过的方式没啥两样。

1. Flex-direction

Flex-direction 属性控制弹性项目沿着主轴排列的方向。

其取值可以是如下四个值之一。

/*这里 ul 代表一个弹性容器*/
ul {
  flex-direction: row || column || row-reverse || column-reverse;
}

用通俗的话来说,flex-direction 属性就是让你决定弹性项目如何布局。要么是水平排放,要么是垂直排放,要么是在两个方向上相反排放。

当然,从技术上讲,水平和垂直都不是弹性盒的世界中方向的叫法。

它们被描述为主轴(main axis)和侧轴(cross axis)。默认如下图。

再次用通俗的话来说,主轴的默认方向是水平的,从左到右。侧轴的默认方向是垂直的,从上到下。

Default main and cross axis

默认情况下,flex-direction 属性被设置为 row,将弹性项目沿着主轴对齐。这就解释了本文开头的无序列表发生了什么。

即使没有显式设置 flex-direction 属性,它也会采用默认值 row

然后,弹性项目会沿着主轴排列,从左到右水平堆放。

flex-items stacked across the main-axis

如果 flex-direction 属性变为 column,那么弹性项目就会沿着侧轴对齐,从上到下堆放,而不再是从左到右堆放。

flex-items stacked across the cross-axis

2. Flex-wrap

flex-wrap 属性的取值可以是如下三个值之一:

// 这里 ul 代表弹性容器
ul {
  flex-wrap: wrap || no-wrap || wrap-reverse;
}

我会通过一个示例来解释 flex-wrap 属性的工作原理。

试着向无序列表中加入更多列表项。

你觉得会怎么样?弹性容器是调整大小以容纳更多列表项,还是把多的列表项分到另一行?

/*再添加 3 个 li 元素*/
<ul> <!--父元素-->
  <li></li> <!--第一个子元素-->
  <li></li> <!--第二个子元素-->
  <li></li> <!--第三个子元素-->
  <li></li>
  <li></li>
  <li></li>
</ul>

幸运的是,弹性容器会自适应容纳新弹性项目。

3 more flex-items added to the unordered list

再多加几个弹性项目试试。给父元素添加一大堆弹性项目,让它总共有 10 个项目。

会发生什么呢?

After adding even more list-items

弹性容器依然会自适应容纳所有子元素,即使浏览器需要水平滚动。

这是每个弹性容器的默认行为。弹性容器会继续在一行上容纳更多弹性项目。

这是因为 flex-wrap 属性默认被设置为 no-wrap,导致弹性容器不会换行。

ul {
    flex-wrap: no-wrap; 
    /*继续容纳更多弹性项目,而不换行*/
}

当然,no-wrap 并非一个铁打不动的值,它是可以换的。

对于这么多弹性项目,你肯定会想让弹性项目在弹性容器内换行。

换行不过是一个好听的词,实际上是说:当弹性容器内的可用空间不能容纳默认宽度的弹性项目时,就分成多行来容纳。

wrap 值就可以实现换行。

ul {
    flex-wrap: wrap;
}

有了这个设置,弹性项目现在就会在需要时分成多行。

在这种情况下,如果一行不再能容纳默认宽度的所有列表项,就会分到多行。即使调整浏览器大小也是如此。

下图是看起来的样子。

注意弹性项目现在是以它们默认的宽度显示。不需要强制多个弹性项目排到一行上。

flex-wrap initiated

还有一个值 wrap-reverse

是的,你猜对了。这个值会让弹性项目分到多行上,不过是以相反的方向。

flex-items wrap in reverse

3. Flex-flow

flex-flowflex-directionFlex-wrap的简写属性,同时带有这两个属性的值。

用过 border 简写属性没有?border: 1px solid red

这里简介的概念是一样的,即多个值在一行内声明。

看看如下的示例:

ul {
    flex-flow: row wrap; /*方向为 "row" 并且项目换行。*/
}

flex-flow broken down in bits

试试其它形式的组合。比如:flex-flow: row no-wrapflex-flow: column wrapflex-flow: column no-wrap

产生的结果与分别设置 flex-directionflex-wrap 属性值所产生的没啥不同的。

我相信你理解这些组合会生成什么。

试一试呗。

4. Justify-content

用弹性盒模型生活真美好。如果你依然怀疑,那么 justify-content 属性会劝服你。

justify-content 属性的取值可以是如下 5 个值之一。

ul {
    justify-content: flex-start || flex-end || center || space-between || space-around
}

那么 justify-content 属性到底会带来什么呢?

好吧,它会让你记起 text-align 属性。

justify-content 属性定义弹性项目如何在主轴上对齐。

来一个简单的示例。考虑如下的简单无序列表:

<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
</ul>

给它加点样式:

ul {
    border: 1px solid red;
    padding: 0;
    list-style: none;
    background-color: #e8e8e9;
}

li {
    background-color: #8cacea;
    width: 100px;
    height: 100px;
    margin: 8px;
    padding: 4px;
}

你应该得到如下结果:

default view after “initiating” flexbox

使用 justify-content 属性,可以把三个弹性项目按你想要的方式在主轴上对齐。

如下是可能的分类。

(1) Flex-start

默认值是 flex-startflex-start 将所有弹性项目向主轴的起点对齐。

ul {
    justify-content: flex-start;
}

justify-content: flex-start (默认行为)

(2) Flex-end

flex-end 将所有弹性项目向主轴的终点对齐。

ul {
    justify-content: flex-end;
}

justify-content: flex-end

(3) Center

Center 像你期望的那样:将弹性项目沿着主轴居中。

ul {
    justify-content: center;
}

justify-content: center

(4) Space-between

Space-between 让每个弹性项目之间保持相同的间距。

ul {
    justify-content: space-between;
}

justify-content: space-between

注意到这里有啥不同么?

看看如下说明图。

(5) Space-around

最后,space-around 让弹性项目两侧的间距保持相同。

ul {
    justify-content: space-around;
}

justify-content: space-around

第二次看就不会受打击了。

看看下面的说明图。

如果这些东西看起来太难把握,不要担心。稍加练习,就会觉得这种语法很舒服。

确保要理解它们是如何影响弹性项目沿着主轴显示的。

5. Align-items

align-items 属性与 justify-content 属性有点相似。

已经理解了 justify-content 属性的话,那么这个属性应该更容易接受。

Align-items 可以被设置为: flex-start || flex-end || center || stretch || baseline

/*ul 代表任意弹性容器*/
ul {
    align-items: flex-start || flex-end || center || stretch || baseline
}

它定义弹性项目如何在侧轴方向对齐。这是 align-items 属性和 justify-content 属性之间的区别。

如下是不同的值如何影响弹性项目。

不要忘记这些属性影响的是哪个方向 - 侧轴。

(1) Stretch

默认值是 stretch。该值会将弹性项目“拉伸”,这样就可以填满整个弹性容器高度。

align-items: stretch

(2) Flex-start

flex-start 会做你期望的事情。它会将弹性项目向侧轴的起点对齐。

align-items: flex-start

(3) Flex-end

不出所料,flex-end 是把弹性项目向侧轴的终点对齐。

align-items: flex-end

(4) Center

center 值同样是可以预测的,它将弹性项目向弹性容器的中间对齐。

align-items: center

(5) Baseline

那么 baseline 值会做什么呢?它将弹性项目沿着项目的基线对齐。

align-items: baseline

基线听起来是真的有点花哨。

结果与 flex-start 看起来很相似,不过有细微的区别。

那么,基线到底是什么鬼东西?下图会有助于理解。

请注意,所有的弹性项目是如何对齐到内容的基线上。

6. Align-content

在讨论 wrap 属性时,还记得在给弹性容器添加更多弹性项目时发生什么了吧?你得到了一个多行的弹性容器。

align-content 属性用于多行弹性容器。

除了没有 baseline 值以外,它的取值与 align-items 相同。

根据定义,它控制在多行弹性容器中的弹性项目的对齐方式。

align-items 一样,它的默认值也是 stretch

现在这些值你应该是熟悉了。所以,如下是它们如何影响带有 10 个弹性项目的多行弹性容器的。

(1) Stretch

使用 stretch 值,弹性项目沿着侧轴伸展填满可用空间。

如下弹性项目之间的间距是由于在项目上设置了 margin

(2) Flex-start

之前你已经看到过 flex-start 值。

这里它是将多行容器内的项目对齐到侧轴的开头。

记住默认的侧轴是从上到下。

因此,弹性项目是对齐到弹性容器的顶端。

(3) Flex-end

flex-end 值将弹性项目对齐到侧轴的末尾。

(4) Center

估计你已经猜到了,center 值将弹性项目对齐到侧轴的中心。

这是最后一个弹性容器属性。理解了如何使用不同的弹性容器属性之后,后面会使用这些属性完成接下来的实践部分。

弹性项目的属性

Order || Flex-grow || Flex-shrink || Flex-basis

在前一小节中,我解释了弹性容器及其对齐属性。的确美妙。相信你正感受到要面临什么。现在我把注意力从弹性容器上移开,带你看看弹性项目及其对齐属性。

像弹性容器一样,弹性项目也有几个对齐属性。OK,来看一看。

1. Order

order 属性允许对弹性容器内的弹性项目重新排序。

总的来说,用 order 属性,你可以把一个弹性项目从一个位置移到另一个位置,就像操作可排序的列表那样。

但是这里是不影响源代码的。也就是说,HTML 源代码中的弹性项目的位置是不用动的。

order 属性的默认值是 0。它可以是负值,也可以是正值。

值得注意的是,弹性项目是根据 order 属性的数字值,从最低到最高来重新排序的。

示例总是管用。考虑如下的无序列表:

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>                          
</ul>

默认情况下,所有的弹性项目的 order 值都是 0

如你所料,在一些基础样式设置后,会得到如下结果:

Default viewing

弹性项目会按照 HTML 源代码中的顺序显示。flex item 1,然后是 2,3,4。

如果因为某些原因,你想让 flex item 1 出现在最后,而且不能修改 HTML 文档中的原顺序,该怎么办呢?

不能修改原顺序,意思是说你不能这样干:

<ul>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>1</li>                      
</ul>

这时候 order 属性就派上用场了。只需把 Flex item 1 的 order 值设置为比其它列表项的 order 值更大就可以了。

如果你曾在块级元素上用过 z-index 属性,肯定对此已经轻车熟路了。

/*选择 ul 内第一个 li 元素 */
li:nth-child(1) {
    order: 1; /*给它一个比 0 大的值*/
}

之后弹性项目就会从最低到最高重新排序。

不要忘记,在默认情况下,第 2、3、4 个列表项的 order 值都是 0。

现在,Flex item 1 的 order 值为 1。

New look after changing the order value for 1

而 flex item 2、3 和 4 的 order 值都是 0。所以,HTML 原顺序没变,就是说没有对默认的显示作出改动。

如果把 Flex item 2 的 order 值设置为 2,会怎么样呢?

是的,你猜对了。它也升上去了。现在它表示弹性项目中最高的 order 值。

Flex-item 2 now has an higher order value

那么如果两个弹性项目有相同的 order 值会发生什么呢?

在下例中,Flex item 1 和 3 被设置了相同的 order 值:

li:nth-child(1) {
    order: 1;
}

li:nth-child(3) {
    order: 1;
}

Flex-item 1 and 3 with the same order value

项目依然是从最低到最高 order 值排列。

不过,Flex item 3 出现在最后,因为它在源文件(HTML 文档)中是出现在 Flex item 1 之后。

当多个弹性项目有相同 order 值时,是基于源文件中的位置重新排序。

现在已经解释够多了。

我要转到其它属性上去。

2. flex-grow 和 flex-shrink

弹性项目的美妙之处在于它是弹性的

flex-growflex-shrink 属性让我们可以将这种灵活性更好地发挥。

flex-growflex-shrink 属性控制弹性项目在有多余空间的时候如何伸展,或者在空间不足的时候如何收缩。

它们的属性取值可以从 0 到任何正值。

下面我来解析一下。

考虑如下简单的无序列表,它只有一个列表项。

<ul>
    <li>I am a simple list</li>
</ul>
ul {
    display: flex;
}

加上点样式后,它显示为这样:

Simple flex-item

默认情况下,flex-grow 属性被设置为 0。言外之意就是弹性项目不会伸展以填满整个可用空间。

值为 0 就像个开关,flex-grow 开关被关掉了。

不过,如果把 flex-grow 值改为 1,如下是发生的事情:

The flex-item grows to fill the available space

弹性项目现在伸展,占据所有可用空间。开关被打开了!

如果试着缩放浏览器,弹性项目也会随之收缩,以适应新的屏幕宽度。

为什么会这样呢?这是因为,在默认情况下,shrink 属性被为值为 1。也就是说 flex-shrink 开关也是开的!

我会深入探讨一下 flex-growflex-shrink 属性,以免你还对理解它们没有自信。

3. Flex-basis

记得我说过弹性项目的美妙之处在于它是弹性的吧?是的,看来你还可以控制它。

在用 flex-grow 或者 flex-shrink 属性调整弹性项目的大小,以适应容器与否之前,可以用 flex-basis 属性指定一个弹性项目的初始大小。

前一句话实际上很重要 - 所以我们会花点时间强调它。

默认值是 flex-basis: autoFlex-basis 可以采用在 width 属性上用过的所有值,即,百分比 || em || rem || 像素 等。

注意,如果要把 flex-basis 属性设置为基于零的值,也要把单位带上。要用 flex-basis: 0px,不能只是 flex-basis: 0

再回到只有一个列表项的示例。

<ul>
    <li>I am a simple list</li>
</ul>
ul {
    display: flex
}

li {
    padding: 4px; /*留点呼吸空间*/
}

默认情况下,弹性项目的初始宽度是被默认值 flex-basis: auto 所影响的。

弹性项目的宽度是根据内容的大小(很显然,如果设置了 padding,就要加上它)自动计算的。

default viewing

就是说,如果弹性项目的内容增加了,它会自动缩放去适应。

<ul>
    <li>I am a simple list AND I am a simple list</li>
</ul>

width is automatically computed

不过,如果想把弹性项目设置为固定宽度,也可以这样做:

li {
    flex-basis: 150px;
}

现在弹性项目已经被强制为 150px 宽度。

flex-item with a constrained width

事情开始变得更好玩了。

4. flex 简写

flex 简写可以让我们一次就设置好 flex-growflex-shrinkflex-basis 属性。

只要合适,我建议用 flex 简写一次设置所有三个属性。

li {
  flex: 0 1 auto;
}

上面的代码等于分别设置三个属性:flex-grow: 0; flex-shrink: 1; flex-basis: auto

请注意顺序。

首先是 Flex-grow,然后是 flex-shrink,最后是 flex-basis。用首字母缩略词 GSB 会有助于记忆。

如果在 flex 简写中未能设置其中一个值,会发生什么呢?

如果只设置了 flex-growflex-shrink 值,flex-basis 会被默认设置为零。这被称为绝对弹性。如果只设置 flex-basis,就得到一个相对弹性

/*这是一个绝对弹性项目*/
li {
  flex: 1 1; /*flex-basis 默认为  0*/
}

/*这是一个相对弹性项目*/
li {
  flex-basis: 200px; /*只设置了 flex-basis*/
}

我知道你在想什么。相对和绝对弹性有什么用?

我会在本文稍后回答这个问题。再信我一次好吧。

下面我们来研究一下一些很有用的 flex 简写值。

(1) flex: 0 1 auto

/* "li" 表示任意弹性项目*/
li {
  flex: 0 1 auto;
}

这跟写 flex: default 是一样的,是所有弹性项目的默认行为。

我先一会儿打茬。

先看看 flex-basis 属性,会更容易理解一些。

flex-basis 被设置为 auto,这意味着弹性项目会根据内容的大小自动决定。

明白了么?

现在转到下一个属性,flex-grow 值是零。就是说 flex-grow 属性不会干预弹性项目的初始宽度。flex-grow 开关是关的。

既然 flex-grow 控制弹性项目的伸展,而它被设置为零,那么弹性项目就不会伸展以适应屏幕。

最后,flex-shrink 值是 1。就是说在必要时会收缩弹性项目

这里是将这条语句应用到一些弹性项目时看起来的样子。

`flex: 0 1 auto`

注意,弹性项目没有伸展。宽度是被自动计算的,如果有必要,弹性项目会随着浏览器缩放而伸缩。

(2) Flex: 0 0 auto

/* "li" 表示任意列表项 */
li {
  flex: 0 0 auto;
}

这与 flex: none 相同。

还是老规矩:宽度是被自动计算,不过弹性项目不会伸展或者收缩(因为二者都被设置为零)。伸展和收缩开关都被关掉了。

它基本上是一个固定宽度的元素,其初始宽度是基于弹性项目中内容大小。

看看这个 flex 简写是如何影响两个弹性项目的。一个弹性项目会比另一个容纳更多内容。

Flex: 0 0 auto

应该注意到的第一件事情是,这两个弹性项目的宽度是不同的。

因为宽度是基于内容宽度而自动计算的,所以这是预料得到的。

试着缩放一下浏览器,你会注意到弹性项目不会收缩其宽度。它们从父元素中突出来了,要看到所有内容,必须横向滚动浏览器。

不要着急,稍后我会展示如何处理这种怪异的行为。

在缩放浏览器时,弹性项目不会收缩,而是从弹性容器中突出来了。

(3) Flex: 1 1 auto

这与 flex: auto 项目相同。

还是按我前面立的规矩。即,自动计算初始化宽度,但是如果有必要,会伸展或者收缩以适应整个可用宽度

伸展和收缩开关打开了,宽度自动被计算。

Flex: 1 1 auto

此时,项目会填满可用空间,在缩放浏览器时也会随之收缩。

(4) Flex: "正数"

这里正数可以代表任何正数(没有引号)。这与 flex: “正数” 1 0 相同。

flex: 2 1 0 与写为 flex: 2 是一样的,2 表示任何正数。

/* "li" 表示任意列表项*/
li {
  flex: 2 1 0; / *与 flex: 2相同 */
}

与前面我立的规矩一样,即,将弹性项目的初始宽度设置为零(嗯?没有宽度?),伸展项目以填满可用空间,并且最后只要有可能就收缩项目

弹性项目没有宽度,那么宽度该如何计算呢?

这个时候 flex-grow 值就起作用了,它决定弹性项目变宽的程度。由它来负责没有宽度的问题。

当有多个弹性项目,并且其初始宽度 flex-basis 被设置为基于零的任何值时,比如 0px,使用这种 flex 简写更实用。

实际发生的是,弹性项目的宽度被根据 flex-grow 值的比例来计算。

我打个茬。

考虑如下两个列表项标记及 CSS:

<ul>
    <li>I am One</li>
    <li>I am Two</li>
</ul>
ul {
    display: flex;
}

/* 第一个弹性项目 */
li:nth-child(1) {
    flex: 2 1 0; /* 与写成 flex: 2 相同*/
}

/* 第二个弹性项目 */
li:nth-child(2){
    flex: 1 1 0;
    background-color: #8cacea;
}

记住设置 flex-grow : 1,会让弹性项目填满可用空间。伸展开关打开了。

这里有两个弹性项目。一个的 flex-grow 属性值是 1,另一个是 2,那么会出现啥情况呢?

两个项目上的伸展开关都打开了。不过,伸展度是不同的,1 和 2。

二者都会填满可用空间,不过是按比例的。

它是这样工作的:前一个占 1/3 的可用空间,后一个占 2/3 的可用空间。

知道是我怎么得到这结果的么?

是根据基本的数学比例。"单项比例 / 总比例",我希望你没有漏过这些数学课。

flex items sharing up space

看到出现啥情况了么?

即使两个弹性项目内容一样大(近似),它们所占空间还是不同。宽度不是基于内容的大小,而是伸展值。一个是另一个的约两倍。

5. Align-self

align-self 属性更进一步让我们更好地控制弹性项目。

你已经看到 align-items 属性是如何有助于整体对齐弹性容器内的所有弹性项目了。

如果想改变一个弹性项目沿着侧轴的位置,而不影响相邻的弹性项目,该怎么办呢?

这是 align-self 属性大展拳脚的地方了。

该属性的取值可以是这些值之一:auto || flex-start || flex-end || center || baseline || stretch

/* 选定第一个列表项*/
li:first-of-type {
    align-self: auto || flex-start || flex-end || center || baseline || stretch
}

这些值你已经熟悉过了,不过作为回顾,如下是它们如何影响特定目标项目。这里是容器内的第一个项目。目标弹性项目是红色的。

(1) Flex-end

flex-end 将目标项目对齐到侧轴的末端。

targeted flex item at the end of the cross axis

(2) Center

center 将目标项目对齐到侧轴的中间。

targeted flex item at the center of the cross axis

3) Stretch

stretch 会将目标项目拉伸,以沿着侧轴填满可用空间。

targeted flex item stretched along the cross axis

4) Baseline

baseline 将目标弹性项目沿着基线对齐。

它与 flex-start 看起来结果是一样的,不过我相信你理解了基线是什么。

我早就解释过了。

targeted flex item aligned along the baseline

5) auto

auto 是将目标弹性项目的值设置为父元素的 align-items 值,或者如果该元素没有父元素的话,就设置为 stretch

在下面的情况下,弹性容器的 align-items 值为 flex-start

这会把所有弹性项目都对齐到侧轴的开头。

目标弹性项目现在继承了 flex-start 值,即父元素的 align-item 值。

targeted flex item aligned along the start of the cross-axis

如下是上面弹性项目的基础样式。这样你可以对发生的事情理解得更好点。

ul {
    display: flex;
    border: 1px solid red;
    padding: 0;
    list-style: none;
    justify-content: space-between;
    align-items: flex-start;  /* 影响所有弹性项目 */
    min-height: 50%;
    background-color: #e8e8e9;
}


li {
  width: 100px;
  background-color: #8cacea;
  margin: 8px;
  font-size: 2rem;
}

现在你差不多已经为有趣的部分做好准备了 :-)

绝对和相对弹性项目

有了前面小节的底子,这里澄清一些重要的概念也是很重要的。

绝对和相对弹性项目之间到底有啥区别呢?

二者之间主要的区别在于间距及如何计算间距。

一个相对弹性项目内的间距是根据它的内容大小来计算的。而在绝对弹性项目中,只根据 flex 属性来计算,而不是内容。

考虑如下的标记。

<ul>
    <li>
        This is just some random text  to buttress the point being explained.
    Some more random text to buttress the point being explained.
    </li>

    <li>This is just a shorter random text.</li>
</ul>

两个列表项元素,一个比另一个的文本多得多。

加点样式:

ul {
    display: flex; /*触发弹性盒*/
}

li {
    flex: auto; /*记住这与 flex: 1 1 auto; 相同*/
    border: 2px solid red;
    margin: 2em;
}

如下是结果:

如果你已经忘了的话,flex: 1 1 auto 是与 flex-grow: 1flex-shrink: 1flex-basis: auto 相同的。

按我之前立的规矩,弹性项目的初始宽度是被自动计算的(flex-basis: auto),然后会伸展以适应可用空间(flex-grow: 1)。

当弹性项目因为被设置为 flex-basis: auto,而导致宽度被自动计算时,是基于弹性项目内包含的内容的大小而计算。

上面示例中弹性项目的内容大小不相同。因此,弹性项目的大小就会不相等。

既然各个宽度开始就不是相等的(它是基于内容的),那么当项目伸展时,宽度也保持不相等。

上面示例中的弹性项目是相对弹性项目。

下面我们把弹性项目变成绝对的 - 就是说这次它们的宽度是基于 flex 属性,而不是内容的大小。

一行代码就可以出奇迹。

li {
    flex: 1 ; /*与 flex: 1 1 0 相同*/
}

看看下面的结果。

这次看到两个弹性项目的宽度相同了吗?

弹性项目的初始宽度是零(flex-basis: 0),并且它们会伸展以适应可用空间。

当有两到多个弹性项目的 flex-basis 取值为零时,它们会基于 flex-grow 值共享可用空间。

这个之前就讨论过了。

现在宽度不会基于内容大小而计算,而是基于指定的 flex 属性值来计算。

这样你就明白了吧。对么?

绝对弹性项目的宽度只基于 flex 属性,而相对弹性项目的宽度基于内容大小。

自动外边距对齐

当心弹性项目上的 margin: auto 对齐

当在弹性项目上使用 margin: auto 时,事情看起来就很怪异了。

你需要理解会发生什么。它会导致不可预料的结果,不过我打算解释解释。

当在弹性项目上使用 margin: auto 时,值为 auto 的方向(左、右或者二者都是)会占据所有剩余空间。

这玩意有点难理解。

下面我来说明一下。

考虑如下的导航栏标记以及 CSS 样式:

<ul>
    <li>Branding</li>
    <li>Home</li>
    <li>Services</li>
    <li>About</li>
    <li>Contact</li>
</ul>
ul {
    display: flex;
}
li {
    flex: 0 0 auto;
}

可以看到如下的结果:

Simple navigation bar

这里有几件事情要注意:

  1. flex-grow 值为设置为零。这就解释了为嘛列表项不会伸展。

  2. 弹性项目向主轴的开头对齐(这是默认行为)。

  3. 由于项目被对齐到主轴开头,右边就有一些多余的空间。看到了吧?

现在在第一个列表项(branding)上使用 margin: auto,看看会出啥情况。

li:nth-child(1) {
    margin-right: auto; /*只应用到右外边距*/
}

margin:auto applied to ‘branding’

刚刚发生了什么?

之前的剩余空间现在已经被分配到第一个弹性项目的右边了。

还记得我前面说的话吧?当在弹性项目上使用 margin: auto 时,值为 auto 的方向(左、右或者二者都是)会占据所有剩余空间

如果想让一个弹性项目的两边都用自动外边距对齐,该怎么办呢?

/* 如果愿意的话,也可以用 margin 简写来设置两个边 */
li:nth-child(1) {
    margin-left: auto;
    margin-right: auto
}

margin:auto applied on both sides of the “branding”

现在空白被分配到弹性项目的两边了。

那么,这是不是对很酷的自动外边距对齐的一种折衷方案呢?

看起来是。如果没注意的话,它也可能是受挫之源。

当在一个弹性项目上使用自动外边距时,justify-content 属性就不起作用了。

例如,在上面的弹性容器上通过 justify-content 属性,设置不同的对齐选项时,对布局没有影响。

ul {
    justify-content: flex-end;
}

justify-content has no impact

实际案例

导航系统是每个网站或者应用程序的重要组成部分。这个世界上的每个网站都会有某种导航系统。

下面我们看看这些热门网站,以及它们是如何实现其导航系统的。

你看到弹性盒是如何帮助你更高效地创建这些布局吗?

也仔细看看哪里会用得上自动外边距特性。

(i) Bootstrap 导航

(ii) AirBnB 桌面导航

(iii) Twitter 桌面导航

建议你自己写代码。

试着自己实现这些导航系统。

现在你已经掌握了所需的所有知识。你所需要的是一点勇气去开始写。

下一节再见。

但愿在你已经完成了导航系统练习之后 :-)

切换 flex-direction 时会发生什么?

提醒一下:路上有一些奇怪的东西。

在入手学习弹性盒模型时,这个部分是最恼火的。

我估计很多弹性世界的新手也会发现如此。

还记得我说过默认的主轴方向是从左到右,侧轴方向是从上到下吧?

default main and cross axis

好吧,现在你也可以改变这个方向。

正如在较早的小节中所描述的那样,用 flex-direction: column 时,确实是这样。

当用 flex-direction: column 时,主轴和侧轴会向如下所看到的那样改变:

New main and cross axis

如果曾用英语写过文字,那么你就知道英语是从左到右,从上到下来写的。

弹性盒的默认主轴和侧轴也是采用同样的方向。

不过,如果将 flex-direction 切换为 column,它就不再遵循英语的范式,而是日语的范式!

是的,日语。

如果你用日语写过文字,那么应该很熟悉了。(郑重声明,我从没用过日语写过文字)。

日文通常是从上到下写的!没那么怪,对吧?

这就解释了为嘛这对英语写作者可能有点恼火。

new main and cross axis

看看下面这个例子。标准无序列表,带有 3 个列表项。不过这次我要改变一下 flex-direction

<ul>
    <li></li>
    <li></li>
    <li></li>
</ul>
ul {
    display: flex;
    flex-direction: column;
}

如下是方向变化之前的样子:

如下是方向变化之后的样子:

出啥事了?

现在文字是以日语风格写的 - 沿主轴从上到下。

我很乐意指出,你会发现一些有趣的事情。

你会看到项目的宽度填满了空间,对吧?

如果在之前要变成这样子,得处理 flex-basis 以及 flex-grow 属性。

下面来看看这些会如何影响新的布局。

li {
    flex-basis: 100px;
}

下面是你会得到的。

Wow — 什么?高度是被影响了,但是宽度没有啊?

我之前说过,flex-basis 属性定义每个弹性项目的初始宽度。

我是错的 - 或者这样说更好:我是用英语来思考。下面我们姑且切换到用日语思考。

并且总是得有宽度。

在切换 flex-direction 时,请注意,影响主轴的每一个属性现在会影响新主轴

flex-basis 这种会影响主轴上弹性项目宽度的属性,现在会影响项目的高度,而不是宽度。

方向已经被切换了!

所以,即使你使用 flex-grow 属性,它也是影响高度。

本质上,每个作用于横向轴(即主轴)上的 flex 属性,现在都会作用于纵向上的新主轴。

它只是在方向上的一个切换。

这里再来一个例子。我发誓在这个例子之后你会有更好的理解。

减少之前看到过的弹性项目的宽度,它们就不再填满整个空间了:

li {
    width: 200px;
}

如果想把列表项移到屏幕中间该怎么办呢?

在英语中,这是你到目前为止处理弹性容器的方式。就是说, 把弹性项目移到主轴的中间

所以,你会用 justify-content: center

但是这样做不起作用。

因为方向变了,中心是沿着侧轴,而不是主轴。

再来看看:

new main and cross axis

所以请用日语文字来思考。

主轴是从上到下,你不需要这样。

侧轴是从左到右。貌似是你所要的。

你需要 把弹性项目移到侧轴的中间

这里想起哪个弹性容器属性了么?

是的,align-items 属性。

align-items 属性处理侧轴上的对齐。

所以要把这些项目移到中间,得这样做:

li {
    align-items: center;
}

瞧瞧!弹性项目已经居中了吧。

flex-items centered in the new direction

是有点悬乎,我知道。如果需要,就再复习一遍好了。

在研究弹性盒模型时,我发现很多 CSS 书都略过了这一部分。

日语文字来思考一下会有很大帮助。

我们有必要了解,所有弹性盒属性都是基于合适的 flex-direction 起作用。

相信你又学到了一点新东西。我很开心解释这些,希望你也很开心 :-)

我的天啦,弹性盒解决了那问题了?

很多设计师用 CSS 时遇到的一些典型问题已经被弹性盒轻而易举解决了。

Philip Walton 在其 弹性盒解决了的问题这个项目 列出了 6 种典型的问题(到本文编写时)。

他全面讨论了之前用 CSS 的限制,以及目前弹性盒提供的解决方案。

建议在完成本文后看一看。

在接下来的实践环节中,在开始用弹性盒创建一个音乐应用布局时,我会解释他提出的一些概念。

Flex 不兼容浏览器的坑

如果你不是在梦中写 CSS 的那一类人的话,就可能想看看这个 Github 仓库

有些比我聪明的人在这里维护了一个弹性盒的 bug 列表及其变通方法。

当有些事情没有按预期起作用时,这是我看的第一个地方。

我也会在后面的实践环节中带你踩踩一些显眼的坑。

所以你是被投保了!

用弹性盒创建一个音乐应用的布局

在学习完了乏味严谨的基础知识之后,该有一些有趣的项目了。

是时候玩玩实际的例子,并把刚获得的弹性盒技能用上去。

想出一个好项目花了我好几天。

由于缺乏创造性的选择,我想出了一个猫玩的音乐应用布局。

我称它为 catty music

也许到了 2036 年,我们就能让猫在火星上的某个地方唱摇滚 :-)

如下是完成了的布局看起来的样子,并且完全是用弹性盒布局的。

可以在线上看,在这里

如果在移动设备上看,外观会稍微有点不同。这是在本文响应式设计一节中要解决的问题。

不过,有件事我得坦白。

我已经做了许多人认为是错误的事情。

我完全用弹性盒创建整个布局。

出于多种理由,这样做可能并非那么理想。但是在这种情况下是故意的。我只是想用一个项目,向你展示所有可以用弹性盒做的事情。

如果你好奇什么时候使用弹性盒模型是对的,什么时候是错的,那你可以读读我写的一篇文章。

Flexbox 很棒,不过它在这里不受欢迎 Flexbox 毋庸置疑是我们大多数人遇到的最好的东西(如果你写 CSS 的话),不过它是不是适合所有情况呢…

这里,我心里的石头终于落地了。现在我相信在读完这个之后,没人会对我大呼小叫了。

Catty Musci 中的一切都是用弹性盒模型布局 - 这是故意尽可能地炫技。

所以下面我们开始把这个玩意建成!

对于任何稍微合理一点的项目来说,有点规划对避免效率低下是有帮助的。

就让我带你看看创建 Catty Music 布局的规划方法。

从哪里开始?

只要用弹性盒创建布局,就应该从找出布局中哪一个部分会作为弹性容器开始。

然后才可以使用弹性盒提供的强大对齐属性。

分析

你可以让整个包含体作为弹性容器(下图中被包含在红色边框内的部分),并把布局的其它部分分成弹性项目(Item 1Item 2)。

这样做完全说得通,让 Item 1 包含布局中除了脚注以外的每个部分。脚注部分用来包含音乐控制按钮。

你知道弹性项目也可以成为弹性容器吗?

是的,是可能的!

你想嵌套多深就嵌套多深(不过理智的做法是保持一个合理的水平)。

于是,根据这个新启示就有了这个...

Item 1(第一个弹性项目)也可以弄成一个弹性容器。然后,侧边栏(Item 1b)和主栏目(Item 1a)就成了 Item 1 的弹性项目。

你依然与我同在,对吧?

像这样拆分布局,会给你一个相当不错的心理模型来处理。

在用弹性盒模型开始创建更为复杂的布局时,你会看到这有多重要。

当然,你并不需要一个像上面那样高大上的图像。在纸上画一个简单的草图就足够了。

记得我说过可以想嵌套多深就嵌套多深吧?貌似这里还可以再来一个嵌套。

看看上面的主栏目(Item 1a)。它也可以变成一个弹性容器,以容纳如下高亮度的部分:Item 1a — AItem 1a — B

可能你会决定不把主栏目(Item 1a)变成弹性容器,只在其内部放入两个 div 来容纳高亮度部分。

是的,这样做没问题,因为 “Item 1a — A” 和 “Item 1a — B” 是垂直堆放的。

默认情况下,div 是垂直堆放的。这是盒模型的工作原理。

如果选择把主栏目变成一个弹性容器,就有了随时可你供调遣的强大对齐属性。

弹性盒中的 Flex 是指弹性、灵活。

弹性容器默认是弹性的,跟响应式有点类似。

这也许是使用弹性容器,而不是普通 div 的另一个原因。不过,这取决于实际情况。

在你创建 Catty Music 时我会论及一些事情事情。你现在应该去写点代码了。

基础 HTML 设置

从如下的基础 HTML 设置开始。

<!DOCTYPE html>
  <html>
  <head>
  <title>Catty Music</title>
  </head>
  <body>

<main></main> <!--用来包含应用的主栏目-->

<footer></footer> <!--用来包含音乐控制按钮和歌曲细节-->

</body>
</html>

然后为它设置样式:

html,
  body {
    height: 100%; /*显式设置这,是很重要的*/
}

body {
    display: flex; /*flex 超能力被激活了! */
    flex-direction: column; /*垂直而不是水平堆放弹性项目(主栏目和脚注元素)*/
}

使用弹性盒模型的第一个步骤就是确定一个弹性容器。

这正好是上面代码所做的。它将 body 元素的 display 属性设置为 flex

现在有了一个弹性容器,即 body 元素。

弹性项目也被定义了(item 1item 2) -  跟前面分析中所做的一样。

注意,如果你对这个概念还是有点模糊,就应该再看看我在之前开始分析时展示的图像。

盯着最后的图像,你应该让弹性项目工作起来。

让脚注吸附在底部

让放音乐控制的脚注吸附在页面的底部,同时让主栏目填满剩余空间。

你怎么实现?

main {
    flex: 1 0 auto; /*填满剩余空间*/
}

footer {
    flex: 0 0 90px; /*不会放大或者收缩 - 只会固定在 90px 高度。*/
}

请看上面列出的代码中的注释。

多亏了 flex-grow 属性,它让主栏目填满整个空间相对很容易。

只需将 flex-grow 属性的值设置为 1 即可。还应该把 flex-shrink 属性设置为 0。为什么呢?

因为 flex-direction 没有改变,所以原因也许不很明显。

在有些浏览器中,会有一个 bug,允许弹性项目收缩后比其内容尺寸小。这是个很古怪的行为。

解决这个 bug 的变通方案是把 flex-shrink 的属性值设置为 0,而不是默认值 1,同时,把 flex-basis 属性设置为 auto

就像是说:请自动计算弹性项目的大小,但是不要收缩

有了这个简写值,就可以得到弹性项目的默认行为。

弹性项目会随着浏览器缩放那个收缩。缩放不是基于 shrink属性,而是基于自动对弹性项目的宽度重新计算(flex-basis: auto)。

这会导致弹性项目至少与它的宽度或者高度(如果声明了)或者默认内容尺寸一样大。

请不要忘记我在分析 flex 简写属性时设立的原则。后面会有更多简写的东西。

现在事情汇集到一起了,下面我们放点样式来定义间距和颜色。

body {
    display: flex;
    flex-direction: column;
    background-color: #fff;
    margin: 0;
    font-family: Lato, sans-serif;
    color: #222;
    font-size: 0.9em;
}

footer {
    flex: 0 0 90px;
    padding: 10px;
    color: #fff;
    background-color: rgba(61, 100, 158, .9);
}

依然没有啥奇迹。

如下是现在你应该有的:

看看事情是如何开始初具规模,你会让它变得更好一点。

固定侧边栏

如果你是跟着写代码,就更新一下你的 HTML 文档。

<main>
  <aside> <!--这代表侧边栏,其中包含了来自 font-awesome 的图标集-->
    <i class="fa fa-bars"></i>
    <i class="fa fa-home"></i>
    <i class="fa fa-search"></i>
    <i class="fa fa-volume-up"></i>
    <i class="fa fa-user"></i>
    <i class="fa fa-spotify"></i>
    <i class="fa fa-cog"></i>
    <i class="fa fa-soundcloud"></i>
  </aside>

  <section class="content"> <!--这一部分会容纳除侧边栏以外的所有东西-->
  </section>
</main>

上面的代码列表已经解释的很清楚了。

至于图标设置,我用了热门的 Font Awesome 库。

这样用你想要的图标就简单到只需添加一个 CSS 类即可。这就是我在 aside 标记中所做的事情。

正如之前解释过的,上面的 main 部分也会成为一个弹性容器。侧边栏(用 aside 标记表示) 以及 section 会成为弹性项目。

main {
  flex: 1 0 auto; /* 变成一个弹性项目*/
  display: flex; /*只包含这一行,现在就有一个弹性容器,带有弹性项目:侧边栏和主内容区*/
}

很好,越来越有意思了,嘿嘿。

现在,主栏目是一个弹性容器了。下面我们来处理它的弹性项目之一,侧边栏。

跟让脚注吸附到页面底部一样,你还会想让侧边栏吸附到页面的左边。

aside {
    flex: 0 0 40px; /*不会放大或者缩小。固定在 40px*/ 
}

侧边栏应该让图标垂直堆放。

可以让侧边栏变成弹性容器,给它设一个 flex-direction,让所有图标垂直堆放。

然后应用一个对齐属性,让图标就位。

在下面的代码列表中,看看你可能会怎么做。

aside {
    /* ...  */
    display: flex; /*现在也是一个弹性容器*/
    flex-direction: column; /*垂直堆放图标*/

    /*因为方向改变了,如下在垂直方向上起作用。*/
    justify-content: space-around;

    align-items: center; /*方向改变了!这条影响的是水平方向。将图标居中*/
    background-color: #f2f2f2; /*让我变漂亮*/
}

aside i.fa {
    font-size: 0.9em;  /*图标的字体大小*/
}

我已经在上面的代码中加了很多注释,现在看看布局是如何漂亮。

很干净,只有几行代码。

合情合理的代码,没有乱七八糟的招数。

Sidebar dealt with nicely

主内容区目前是空的。不要忘记它是第二个列表项,侧边栏是第一个。

给这里放一下东西。

给主内容区添加内容

你可以再看看完工的项目,这样就不会忘记接下来要发生的事情。

更重要的是,这能帮助你理解下面的代码。

更新 HTML 文档,在 .content 部分添加如下标记:

<section class="content"> <!--这一区是空的,用内容填充它-->

    <div class="music-head"> <!--第一个列表项:包含音乐详情-->

        <img src="images/cattyboard.jpg" /><!--专辑封面-->

        <section class="catty-music"> <!--专辑的其它细节-->
            <div>
                <p>CattyBoard Top 100 Single Charts (11.06.36)</p>
                <p>Unknown Artist</p>
                <p>2016 . Charts . 100 songs</p>
            </div>

            <div> <!--Music controls-->
                <i class="fa fa-play">Play all</i>
                <i class="fa fa-plus">Add to</i>
                <i class="fa fa-ellipsis-h">More</i>
            </div>
        </section>

    </div> <!--end .music-head-->

    <!--第二个列表项:包含专辑中的歌曲列表-->
    <ul class="music-list">  
        <li>
            <p>1. One Dance</p>
            <p>Crake feat CatKid & Cyla</p>
            <p>2:54</p>
            <p><span class="catty-cloud">CATTY CLOUD SYNC</span></p>
        </li>
        <li>
            <p>2. Panda</p>
            <p>Cattee</p>
            <p>4:06</p>
            <p><span class="catty-cloud">CATTY CLOUD SYNC</span></p>
        </li>
        <li>
            <p>3. Can't Stop the Feeling!</p>
            <p>Catin Cimberlake</p>
            <p>3:56</p>
            <p><span class="catty-cloud">CATTY CLOUD SYNC</span></p>
        </li>
        <li>
            <p>4. Work From Home</p>
            <p>Cat Harmony feat Colla</p>
            <p>3:34</p>
            <p><span class="catty-cloud">CATTY CLOUD SYNC</span></p>
        </li>
    </ul>
</section>

嗯,我比上次添加了更多的东西,不过很简单。

我用一个 div 填充空内容部分,这个 div 用来容纳 catty 专辑的封面和其他细节。

ul 容纳专辑的歌曲列表。

列表中用单独的段落来容纳歌曲标题、艺术家、时长、"_catty cloud sync“。

那么你打算如何设置样式呢?

看看我怎么做的?

首先,应该让 .content 部分成为弹性容器。

.content {
    display: flex;
    flex: 1 0 auto; /*这确保本区伸展到填满可用空间*/
    flex-direction: column;
}

还应该处理它的弹性项目:

.music-head {
    flex: 0 0 280px; /*相同的备忘,不要伸展或收缩 - 固定为 280px*/
    display: flex;  
    padding: 40px;
    background-color: #4e4e4e;
}

.music-list {
    flex: 1 0 auto;
    list-style-type: none;
    padding: 5px 10px 0px;
}

.music-head 容纳专辑封面以及其它专辑细节。

相同的备忘录,不要伸展或者收缩,保持高度为 280px。

高度?没有宽度?是的!

父元素已经切换了 flex-direction

随后需要让它变成一个弹性容器,所以写上 display: flex

.music-list 容纳歌曲列表,填满与上面的 .music-head 共享的剩余可用空间。

这依然没让人觉得有多漂亮,不过来吧,如果你还跟着,你做的很不错了。

赞一下。

catty music- unfinished

这里有几个问题。

  1. 歌曲列表看起来很糟糕。

list of songs

  1. 包含音乐封面的部分有真的很难看的文本

Ugly looking music art texts

我会再次来你解决这些问题。

下面是我提出的解决方案。

处理歌曲列表

每个歌曲列表包含 4 个段落,歌名、艺术家、时长和 “catty cloud sync”。

一定有办法让所有这些放在一行,每个段落占据该行相等空间。

用弹性盒来搞定!

这里的概念与很多栅格系统中用的一样。

翻译为代码:

li {
  display: flex; /*段落现在显示在一行上*/
  padding: 0 20px; /*留点呼吸空间*/
  min-height: 50px;
}

li p {
  flex: 0 0 25%; /*这是甜面酱*/
}

看到段落会发生什么了吗?

flex: 0 0 25%;

“不要伸展或者收缩,不过每个段落应该占据 25% 的可用空间”

段落所占空间是相等的。

使用这种技术

这种技术是很有用的。可以用它来创建不相等的内容区。比如,两栏视图。

一个栏目可以占可用空间的 60%,另一个占 40%:

.first-section: 0 0 60%;
.second-section: 0 0 40%;

可以用这种技术创建栅格系统。

如下是现在列表应该的样子。

List of songs fixed

给列表交替的颜色,也处理一下 “catty cloud sync” 标签。

li span.catty-cloud {
  border: 1px solid black;
  font-size: 0.6em;
  padding: 3px;
}

li:nth-child(2n) {
  background-color: #f2f2f2;
}

所以,你征服它了,开始更好理解弹性盒方言了。

这是你现在应该得到的东西了。

Catty music — almost done

现在要处理第二个问题了。

让相册详情文本看着更好看点

下面真是很简单的事情。

.catty-music{
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
  font-weight: 300;
  color: #fff;
  padding-left: 50px;
}

.catty-music div:nth-child(1){
  margin-bottom: auto;
}

.catty-music div:nth-child(2){
  margin-top: 0;
}

.catty-music div:nth-child(2) i.fa{
  font-size: 0.9em;
  padding: 0 0.7em;
  font-weight: 300;
}
.catty-music div:nth-child(1) p:first-child{
  font-size: 1.8em;
  margin: 0 0 10px;
}

.catty-music div:nth-child(1) p:not(:first-child){
  font-size: 0.9em;
  margin: 2px 0;
}

你做到了,而且做的相当不错。

Music art texts looking much better

一个快速练习

我留下脚注部分给你作为练习。

试着自己解决脚注。只需采用相同的技术。你知道你能搞定吗?

如果卡住了,可以查看 Catty Music 的完整源代码。

你也可以把整个脚注分成弹性项目,从这里开始。

Footer explained

Wow。简直不相信你到了这一步。不错!现在你成了一个弹性盒忍者。

下面,你会看到弹性盒是如何有助于响应式设计。

弹性盒用于响应式设计

已经有不少关于响应式设计的书,有不少书还不错。

因为本文专注于弹性盒模型,所以我不会深入响应式设计的一般状态。

像我之前在某处说过的那样,用弹性盒模型,我们确实得到了一些开箱即用的响应性。

弹性盒就像 弹性的盒子

不过,可以通过媒体查询搞定不同的屏幕尺寸,然后改变弹性行为。

如下是一个示例。我们又用简单无序列表来帮忙。

<ul>
    <li>Home</li>
    <li>About</li>
    <li>Contact</li>
    <li>Register</li>
    <li>Login</li>
  </ul>

并且设置点样式...

ul {
    list-style-type: none;
    display: flex;
    border: 1px solid #4e4e4e;
}

li {
    flex: 0 0 auto;
    padding: 10px;
    margin: 10px;
    background-color: #8cacea;
    color: #fff;
    font-size: 1em;
}

现在你已经是弹性盒专家了,所以你知道是咋回事。

如下是导航栏的样子。

Flexbox Navigation

这对于桌面以及平板电脑可能还挺酷,不过在某些屏幕尺寸上,就特别难看。

在移动设备上,你会想让导航条目垂直堆放。

然后媒体查询就登堂入室了。

@media screen and (max-width: 769px) {
    /* 这里的代码只适用于宽度小于 769px 的屏幕设备*/
    ul {
        flex-direction: column; /* 在更小的设备上,切换方向*/
    }
}

Navigation bar for mobile devices

如果在这之前,你对响应式设计还懂那么点,那就太棒了。

把弹性盒模型纳入你已有的知识好了。

顺便说一句,我假设你理解媒体查询是什么。

如果不理解的话,看看下面的简介。

媒体查询

媒体查询是响应式设计的核心,让你可以以特定屏幕尺寸为目标,单独指定运行在该设备上的代码。

使用媒体查询最流行的形式是 @media 规则。

它看起来是这样的:

@media screen and (max-width: 300px) {
  /*在这个代码块中写 CSS*/
}

看着这段代码,猜都可以猜到它的作用。

"对于最大宽度为 300px 的屏幕设备… 做这做那"

在该代码块中的任何样式都将适用于匹配表达式的设备,即 “screen and (max-width: 300px)”

我猜这有助于消除一些疑惑。

快速练习

Catty music 在移动设备上的显示有所不同。这可是个好消息。更棒的是你应该试着重新创建它。

mobile catty music

如果你遇到了难题,本教程代码库的链接在下一节。解决方案也在仓库中。

快到最后了!

在总结部分,我将讨论浏览器支持、有用的链接和帮助你上手的资源。

总结

你已经学习了如何使用弹性容器和弹性项目的对齐属性。

我引导你理解了绝对和相对弹性、自动外边距对齐以及切换弹性方向。

你还有机会将弹性盒技能应用到创建 Catty Music 应用,然后我也提到了响应式设计。

这确实是一段漫长的旅程。

现在,我会向你解释一些最终的概念。用一些我认为你会发现很有用的资源和链接帮助你。

弹性盒的浏览器支持

这是当倾向于在生产中使用弹性盒模型时,被问到的一个常见的问题。

这问题我也没法很好回答,不过 caniuse 网站能公平处理这个问题。

如下是一个来自 caniuse 的屏幕截图,浏览器支持很棒。你可以在这里自己看。

在我职业生涯早期,我浏览过 caniuse 很多次,依然没法掌握表示的数据是什么意思。所以这里有一个简单的解释。

caniuse 网站的右下角是一个图例。

caniuse legend

看看上面的图像,或者就访问一下网站,找到图例,就明白了。

实际上就是这么回事。

附加资源

希望你会发现这些对你有帮助:

  1. 完整的《理解弹性盒》这篇文章的 PDF 文档 — 直接链接

  2. 在线玩玩 Catty Music 的代码

  3. 理解弹性盒教程的完整代码库

  4. 精通现代 CSS 布局

  5. 弹性盒游乐场及代码生成器

  6. 弹性盒青蛙:一个很酷的弹性盒游戏

最后,我得感谢你跟着学到这里。

我正在写一本有关 CSS 现代布局的书,我认为这本书也许是这方面最重要的书籍之一。这本书以使用弹性盒和网格 CSS 模型为中心。

如果你想看看我的书,就到这里来看看吧!

相关文章