为什么是Flexbox?
长久以来,唯一可用且有稳定的跨浏览器兼容性的能用来构建CSS布局的工具只有floats和positioning。它们是即可行也不错,但是在某些方面就有限制,也难搞。
以下简单的布局要求是难以或不可能用这样的工具方便且灵活的实现:
垂直居中父内容的里一块内容。
使容器内所有子项占据等量的可用宽度/高度,而不管有多少宽度/高度可用。
使多列布局中的所有列采用相同的高度,即使它们包含的内容量不同。
一个简单的例子
在本文中,我们将通过一系列练习来帮助你了解 Flexbox 的工作原理。要开始,您应该拷贝一个文件到本地 — mozila github 仓库的 flexbox0.html — 在现代浏览器里打开它(比如 Firefox、Chrome),然后打开你的编辑器看一眼它的代码。你可以看它的线上实例。
你可以看到这个页面有一个含有顶级标题的<header> 元素,和一个包含三个<article>的<section> 元素。我们将使用这些来创建一个非常的标准三列布局,如下所示:
指定元素的布局为 flexible
首先,我们需要选择将哪些元素将设置为 flexible 框。我们要设置那些 flexible 元素的父元素display
为一个特定值。在本例中,我们想要设置 <article>元素,因此我们给<section>元素(变成了 flex 容器)设置 display:
section {
display: flex;
}
所以,就这样一个简单的声明就给了我们所需要的一切—不可思议,对吧? 我们的多列布局具有大小相等的列,并且列的高度都是一样。 这是因为这样的 flex 项(flex容器的子项)的默认值是可以解决这些的常见问题的。 后面还有更多内容。
注意:假如你想设置行内元素为 flexible box,也可以置 display
属性的值为 inline-flex。
flex 模型说明
当元素表现为 flex 框时,它们沿着两个轴来布局:
主轴(main axis)是沿着 flex 元素放置的方向延伸的轴(比如页面上的横向的行、纵向的列)。该轴的开始和结束被称为** main start** 和** main end。
横轴(cross axis)是垂直于 flex 元素放置方向的轴。该轴的开始和结束被称为 cross start 和 cross end。
设置了 display: flex 的父元素(在本例中是 <section>) 被称之为 flex 容器(flex container)。
在 flex 容器中表现为 flexible 框的元素被称之为 flex 项(flex item**)(本例中是 <article>元素。
列还是行?
Flexbox 提供了 flex-direction
这样一个属性,它可以指定主轴的方向(flexbox 子类放置的地方)— 它默认值是 row,
这使得它们在按你浏览器的默认语言方向排成一排(在英语/中文浏览器中是从左到右)。
尝试将以下声明添加到 section 元素的 css 规则里:
flex-direction: column
你会看到,这会将那些元素设置为列布局,就像我们添加这些 CSS 之前。在继续之前,请从示例中删除此规则。
注意:你还可以使用 row-reverse 和 column-reverse 值反向排列 flex 项目。用这些值试试看吧!
换行
当你在布局中使用定宽或者定高的时候,可能会有一个问题出来即处于容器中的 flexbox 子元素会溢出,破坏了布局。你可以看一下 flexbox-wrap0.html 示例(你也可以拷贝到本地),如下所示:
在这里我们看到,子代确实超出了它们的容器。 解决此问题的一种方法是将以下声明添加到 section css 规则中:
flex-wrap: wrap;
现在尝试一下吧;你会看到布局比原来好多了
现在我们有了多行 flexbox — 任何溢出的元素将被移到下一行。在 article 元素上设置的 flex: 200px 规则,意味着每个元素的宽度至少是200px;我们将在后面更详细地讨论这个属性。你可能还注意到,最后一行上的最后几个项每个都变得更宽,以便把整个行填满。
但是这里我们可以做得更多。首先,改变 flex-direction
属性值为 row-reverse
— 你会看到仍然有多行布局,但是每一行元素排列的方向和原来是相反的了。
flex-flow 缩写
到这里,应当注意到存在着 flex-direction
和 flex-wrap
— 的缩写 flex-flow
。比如,你可以将
flex-direction: row;
flex-wrap: wrap;
替换为
flex-flow: row wrap;
flex 项的动态尺寸
现在让我们回到第一个例子,看看是如何控制 flex 项占用空间的比例的。打开你本地的 flexbox0.html,或者拷贝 flexbox1.html 作为新的开始(查看线上)。
先,将以下规则添加到 CSS 的底部:
article {
flex: 1;
}
这是一个无单位的比例值,表示每个 flex 项沿主轴的可用空间大小。本例中,我们设置<artticle>元素的 flex 值为 1,这表示每个元素占用空间都是相等的,占用的空间是在设置 padding 和 margin 之后剩余的空间。因为它是一个比例,这意味着将每个 flex 项的设置为 400000 的效果和 1 的时候是完全一样的。
现在在上一个规则下添加:
article:nth-of-type(3) {
flex: 2;
}
现在当你刷新,你会看到第三个<article>元素占用了两倍的可用宽度和剩下的一样 — 现在总共有四个比例单位可用。 前两个 flex 项各有一个,因此它们占用每个可用空间的1/4。 第三个有两个单位,所以它占用2/4或这说是1/2的可用空间。
您还可以指定 flex 的最小值。 尝试修改现有的 article 规则:
article {
flex: 1 200px;
}
article:nth-of-type(3) {
flex: 2 200px;
}
这表示“每个flex 项将首先给出200px的可用空间,然后,剩余的可用空间将根据分配的比例共享“。 尝试刷新,你会看到分配空间的差别。
Flexbox 的真正价值可以体现在它的灵活性/响应性,如果你调整浏览器窗口的大小,或者增加一个<article>元素,这时的布局仍旧是好的。
flex: 缩写与全写
flex
是一个可以指定最多三个不同值的缩写属性:
第一个就是上面所讨论过的无单位比例。可以单独指定全写 flex-grow
属性的值。
第二个无单位比例 — flex-shrink
— 一般用于溢出容器的 flex 项。这指定了从每个 flex 项中取出多少溢出量,以阻止它们溢出它们的容器。 这是一个相当高级的flexbox功能,我们不会在本文中进一步说明。
第三个是上面讨论的最小值。可以单独指定全写 flex-basis
属性的值。
我们建议不要使用全写属性,除非你真的需要(比如要去覆盖之前写的)。使用全写会多些很多的代码,它们也可能有点让人困惑。
水平和垂直对齐
还可以使用 flexbox 的功能让 flex 项沿主轴或横轴对齐。让我们一起看一下新例子 — flex-align0.html(在线浏览)— 我们将会有一个整洁,灵活的按钮/工具栏。 此时,你看到了一个水平菜单栏,其中一些按钮卡在左上角,就像下面这样:
首先,拷贝一份到本地。
然后,将下面的 CSS 添加到例子的底部:
div {
display: flex;
align-items: center;
justify-content: space-around;
}
刷新一下页面,你就会看到这些按钮很好的垂直水平居中了。我们是通过下面所说的两个新的属性做到的。
align-items
控制 flex 项在横轴上的位置。
默认的值是 stretch,其会使所有 flex 项沿着横轴的方向拉伸以填充父容器。如果父容器在横轴方向上没有固定宽度(即高度),则所有 flex 项将变得与最长的 flex 项一样长(即高度保持一致)。我们的第一个例子在默认情况下得到相等的高度的列的原因。
在上面规则中我们使用的 center 值会使这些项保持其原有的高度,但是会在横纵居中。这就是那些按钮垂直居中的原因。
你也可以设置诸如 flex-start 或 flex-end 这样使 flex 项在横轴的开始或结束处对齐所有的值。
查看 align-items
了解更多。
你可以用 align-self
属性覆盖 align-items
的行为。比如,你可以这样:
button:first-child {
align-self: flex-end;
}
去看看它产生的效果,然后删除它
justify-content
控制 flex 项在主轴上的位置。
默认值是
flex-start,
这会使所有 flex 项都位于主轴的开始处。
你也可以用 flex-end 来让 flex 项到结尾处。
center
在 justify-content 里也是可用的,可以让 flex 项在主轴居中。
而我们上面用到的值 space-around 是很有用的
—它会使所有 flex 项沿着主轴均匀地分布,在任意一端都会留有一点空间。
还有一个值是
space-between,它和
space-around 非常相似,只是它不会在两端留下任何空间。
在继续下面之前,多多使用提到过的属性吧,看看它们的效果。
flex 项排序
Flexbox 也有可以改变 flex 项的布局位置的功能,而不会影响到源顺序(即 dom 树里元素的顺序)。这也是传统布局方式很难做到的一点。
代码也很简单,将下面的 CSS 添加到示例代码下面。
button:first-child {
order: 1;
}
刷新下,然后你会看到 "Smile" 按钮移动到了主轴的末尾。下面我们谈下它实现的一些细节:
所有 flex 项默认的 order
值是 0。
order 值大的 flex 项比 order 值小的在显示顺序中更靠后。
相同 order 值的 flex 项按源顺序显示。所以假如你有四个元素,其 order 值分别是2,1,1和0,那么它们的显示顺序就分别是第四,第二,第三,和第一。
第三个元素显示在第二个后面是因为它们的 order 值一样,且第三个元素在源顺序中排在第二个后面。
你也可以给 order 设置负值使它们比值为 0 的元素排得更前面。比如,你可以设置 "Blush" 按钮排在主轴的最前面:
button:last-child {
order: -1;
}
flex 嵌套
flexbox 也能创建一些颇为复杂的布局。设置 flex 项为 flex 容器也是没有什么问题的,它的孩子也就表现为 flexible box 了。看一下 complex-flexbox.html(在线浏览)。
这个例子的 HTML 是相当简单的。我们用用一个<section>元素包含了三个 <article>。第三个 <article>元素包含了三个<div>:
section - article
article
article - div - button
div button
div button
button
button
现在让我们看一下布局用到的代码。
首先,我们设置<section>的子代布局为 flexible box。
section {
display: flex;
}
下面我们给<article>元素设置一些 flex 值。特别注意这里的第二条规则—我们设置第三个 <article>元素里的子元素同样表现为 flex 项,但是这次我们使它们放置为列。
article {
flex: 1 200px;
}
article:nth-of-type(3) {
flex: 3 200px;
display: flex;
flex-flow: column;
}
接下来,我们选择了第一个<div>首先使用 flex: 1 100px; 简单的给它一个最小的高度 100px,然后设置它的子代(<button> 元素)为 flex 项。在这里我们将它们放在一个包装行中,使它们居中对齐,就像我们在前面看到的单个按钮示例中所做的那样。
article:nth-of-type(3) div:first-child {
flex: 1 100px;
display: flex;
flex-flow: row wrap;
align-items: center;
justify-content: space-around;
}
最后,我们给按钮设置大小,有意思的是我们给它一个值为1的 flex 属性。如果你调整浏览器窗口宽度,你会看到这是一个非常有趣的效果。按钮将占用尽可能多的空间,尽可能多的坐在同一条线上,但是当它们不再适合在同一条线上,他们会到下一行去。
button {
flex: 1;
margin: 5px;
font-size: 18px;
line-height: 1.5;
}
跨浏览器兼容性
大多数浏览器都支持 Flexbox,诸如 Firefox, Chrome, Opera, Microsoft Edge 和 IE 11,较新版本的 Android/iOS 等等。但是你应该要意识到仍旧有被人使用的老浏览器不支持 Flexbox(或者支持,但是只是支持非常非常老版本的 Flexbox)。
虽然你只是在学习和实验,这不太要紧; 然而,如果您正在考虑在真实网站中使用flexbox,则需要进行测试,并确保在尽可能多的浏览器中您的用户体验仍然可以接受。
Flexbox 相较其他一些 CSS 特性可能更为棘手。 例如,如果浏览器缺少 CSS 阴影,则该网站可能仍然可用。 但是假如不支持 flexbox 功能就会完全打破布局,使其不可用。
我们将讨论在未来的模块中克服棘手的跨浏览器支持问题的策略。
总结
到这里,介绍flexbox的基础知识就结束了。 我们希望你体会到乐趣,并且玩的开心,能随着你的学习与你一起向前。 接下来,我们将看到CSS布局的另一个重要方面—网格系统。