现代 CSS

今天就用Flexbox

特别声明:如果您喜欢小站的内容,可以点击申请会员进行全站阅读。如果您对付费阅读有任何建议或想法,欢迎发送邮件至: airenliao@gmail.com!或添加QQ:874472854(^_^)

Flexbox让我们对过去无法真正达到的对布局的掌控更进了一层,过去我们浮动、清除浮动,与inline-block间的空格斗争,强制display:table,甚至用position:absolute来撑开内容。现在我们不需要依赖这些解决方案使得内容在不支持伸缩特性的浏览器可见。Flex的特性给我们所以构建的内容增加了一系列重要工具,但不是替代之前存在的东西,而是改进我们如今的构建方式。

我认为Flexbox的主要挑战在于当今构建的内容和未来将要构建的内容之间有明显差距。大家普遍的态度是“至少还要再过x年”,或者是“我们要等X浏览器支持”,然而我们不需要再这样思考。

我不怪人们觉得Flexbox难以掌握。现在网页上大多数教程展示一堆完全抽象的箱子,或者直接跳得太远以致于唯一合理的关于Flexbox的是回过头来使用那些简单奇妙的布局,并最终陷入成堆不可维护的代码中。我之前的文章是模型,用来理解Flexbox。

这个系列教程都有一个目的,要么展示属性以及它们的作用,要么用未来的可能性来鼓舞你。我认为它们成功得做到了这两点,但是仍然存在空缺-我们需要一个合理,实用的方法从而在今天就开始使用Flexbox。

Flexbox之路

在这个实验里,我尝试着展现Flexbox在当今布局中的价值,概括出添加flexbox的策略并且给出几个例子来展示Flexbox如何给当今的布局增加之前无法或者很难达到的额外能力。另外我也会整理一些在实现过程中常见的错误因为我相信有时候能够排查出新技术的任何问题比学习新东西更难。我的目标是有更多人开始用Flexbox只有这样我们才能突破限制,并趁早消除bug。

如果还不熟悉Flexbox,这里Flexbox 探险是一部分之前的实验,通过这些我尝试着理解它如何计算空间以及未来可能如何被使用。里面的链接都很有用,可以帮助你加速学习Flexbox。

如果对Flexbox一点了解都没有,或者从未深入了解过Flexbox,建议点击这里,阅读相关教程。

让布局更美好

Flexbox的属性包含了完整的对齐和排序的控制,一旦习惯了它的工作方式,就会清晰得发现只需要写很少的CSS就能增强如今我们构建东西的方式。

我们是否应该完全抛弃float, inline-block ,或者display table呢?不,并且我认为教育者依然应该教给学生们这些东西即便它们最终被淘汰,有一点非常重要就是有时候我们打破了CSS的限制,这时会产生很多有趣的发现。

下面是一些我认为通过添加Flexbox可以很容易得改进的布局例子。

卡片布局

卡片布局

卡片布局很热门,但是要得到等高列就需要设置最小高度,或者把列表分成多行。如果不分成多行,必须在每一行后都清除浮动。Flexbox让我们轻松解决这些问题。

分屏布局

分屏布局

分屏布局

分屏布局的一个难点在于它依赖最小高度或绝对定位来拉伸,屏幕不同尺寸时很麻烦。Flexbox可以让每个项目等高,并且可以非常方便得垂直居中。

固定布局

固定布局

Zoe Gillenwater在这篇文章中介绍了使用Flexbox创建一个固定布局。

Flexbox中使用space-between属性可以完成这样的布局,并且有排序和对齐的选项。

报纸或广告单元

报纸或广告单元

报纸布局和广告单元的好处是排序和对齐。这只是一个概念,也许实际上很愚蠢-但试想如果一个系统可以检测你从来没阅读有关体育的部分-然后重新排序把体育排到页面底部或许把世界新闻排到顶部。对一部分进行重排可以按读者的阅读偏好进行重组。通过Flexbox这些变得难以置信得简单。

多栏布局

多栏布局

像我一样,你认为CSS columns可以完成这个布局,但是它会破坏内容块-Flexbox是我们可以在多栏中分隔出列表而不需要用标签去分隔列表。

仪表盘

仪表盘

仪表盘是另一个关于排序的例子,并且栏目包裹也非常方便,或者等高栏目。

拥抱改变

Flexbox的目标是解决之前需要hack实现的布局问题,同时也让我们可以拥抱未来可变的内容和设计。还可以让我们适应那些对于不参与的人看起来简单设计需求……未来,我们会有更多的工具例如CSS Grid,但是现在Flexbox已经准备好了并且等着被马上使用,我们不需要在用已经用了13年的破旧锤子敲打每个布局。

内容是灵活的

内容本质上是灵活的,过去我们的设计,结构都迫使内容适应设计而不是让设计和结构适应内容。即便今天,我们基于结构约束强加限制,我们什么时候才应该基于内容策略施加约束而不是开发者策略呢。

我们不是总有控制权

内容管理系统是一个我们放弃布局灵活性控制的基本例子-我们经常用有限的知识来设计什么样的内容将呈现在一个空间内。我们通过所有类型的hackery来实现,或者强制使用特定规则 - 但这样做时我们失去了灵活性。一些实现事物视觉化的方法使得我们抛弃了语义化的标记并且当我们停止构建语义化的事物时我们需要费更多力气让它们易于访问。

更多设计工具

设计通常告诉我们要构建的事物以及如何构建,然后开发工具和限制告诉我们设计可以实现的能力。理想情况下,对Flexbox的理解越成熟让我们的创造质量更高,设计更多变。

道路是曲折的

只有当我们完全理解一样事情之后才能开始超越它最初的目的来实现一些更棒的事情,如何打破它影响着如何完成。

如今,我们了解了Flexbox的基本规则以及在今天的布局上能够做的,通过培养我们可以影响整个新的布局,直到我们看到Flexbox和其他一些更少使用的方式结合在一起,或有了新的特性我们才开始看到可能性出现。

Flexbox中的策略

为了让Flexbox更容易接受,我打算评判一些准则来让它更容易完成。

构建我们一直以来想做的

我们应该能够回顾我们以前完成的任何项目并且添加一些代码就可以得到Flexbox的好处。我们应该可以在未来像以往一样创建新的项目,添加一些代码使Flexbox生效并让布局更美好。

使用例如Modernizr之类的库我们可以在.flexbox后添加新的flexbox代码,选择性得在.no-flexbox后添加任何用float声明的布局(float:left,width:33% 等等)直到某天我们可以逐步停止。

最小化代码

管理3种语法加上浮动布局只为了用Flexbox好像很疯狂。使用前缀是采用Flexbox策略中的一个必须的部分。

让它变得有价值

我们需要布局受益于Flexbox否则我们为了实现它使用不必要的代码布局。这些明显涉及到垂直居中,栏目尾部对齐,以及排序。

Cut the mustard: Flex-wrap

Flex-wrap是Flexbox实用的点之一,

我看到有些人这个时候使用@supports,但这个属性很多浏览器还不支持,我更建议编写自己的特征检测或只使用Modernizr。

提高Flexbox

为了更好理解把现有的布局修改成flexbox有多容易这里有两个实验布局改进 - 一个是卡片图案一个是分屏模式。

flexbox如何让它更美好

我们已经有了一个方式例如floats来完成这个布局,但每个项目显示出不同的高度,所以你必须给每个项目强制一个最小或固定高度来获得等高栏目,并且要把一个按钮固定在底部你需要给它position absolute,然后在底部填满卡片。

一旦完成了所有,需要做的事情是把屏幕宽度改变到发生倒塌的临界点让你感受一下你刚才创建的怪兽的带来的破坏。许许多多改变最小宽度或固定高度,其他人要在做的就是比你的设计增加一点点内容你就会处于一个充满痛苦的世界。

Flexbox解决了这个问题,我们将构建一个不需要设置这些限制的布局(栏目不是等高的),添加Flexbox然后我们在几乎每个现代浏览器里对齐都不错。

第1步:标记结构

我认为这个图案是一些列无须卡片,每个卡片是在列表项里的独立模块。

在这个卡片里有几个内容项(图片,标题,段落,按钮)。个人偏好在不同屏幕的垂直方向上拉伸内容的方法是把内容和图片分离这样它们可以在float布局中一个接一个排列。

As I mentioned previously, I'm building how I would've built without Flexbox, it shouldn't really affect my markup structure.

像我之前提到的,我没有用Flexbox来构建,它不应该影响到标签结构。

<!-- card list -->
<ul class="flex-card-list">
    <!-- card list item -->
    <li class="flex-card-listitem">
        <!-- card module -->
        <div class="flex-card">
            <!-- image container -->
            <div class="flex-card-image">
                <img src="/img/placeholder.jpg" />
            </div>
            <!-- content container -->
            <div class="flex-card-content">
                <h3 class="flex-card-heading">First</h3>
                <p>I'm a card and I'm first.</p>
                <a href="#" class="flex-card-button">Button</a>
            </div>
        </div>
    </li>
</ul>

第2步:给列表添加样式

在无序列表上我们添加display:flex,任何时候用display:flex表示我们想要对这个元素的子元素做些事情,这个例子中我们的目标是让列表项高度相等。Flex会默认把项目垂直拉伸,水平铺开。拉伸列表项是让嵌套项拉伸到相同尺寸的第一步。

我们的卡片列会不止一列或行,所以我们需要用flex-wrap让项目在遇到容器边缘时换行。

/* Put behind .flexbox for Feature detection with Modernizr */
.flexbox .flex-card-list {
    display:flex;
    flex-wrap:wrap;
}

第3步:给列表项添加样式

我们给列表项添加display:flex因为我们想要卡片模块被flexbox操纵。

.flexbox .flex-card-listitem {
    display:flex;
}

第4步:媒体查询

这时,我们为不同屏幕宽度添加媒体查询。我的布局是每行三个卡片,每行两个卡片,然后是每行一个卡片。

/* Code to adjust the layout to 2 cards per row */
@media all and (min-width:40em) {
    /* Float layout */
    .flex-card-list li {
        width:50%;
    }
    .no-flexbox .flex-card-list li {
        float:left;
    }
}
/* Code to adjust the layout to 3 cards per row */
@media all and (min-width:40em) and (max-width:60em) {
    /* Clears the row for 2 item per row layout */
    .no-flexbox .flex-card-list li:nth-child(2n+1) {
        clear:both;
    }
}
@media all and (min-width:60em) {
    /* common to flex and non-flex */
    .flex-card-list li {
        width:33.33%;
    }
    /* Float specific: Clear after every third item */
    .no-flexbox .flex-card-list li:nth-child(3n+1) {
        clear:both;
    }
}

第5步:卡片模块

现在我们有一列根据媒体查询改变布局的对象,每个项目都被拉伸到列表项的高度。我们有了等高项,但是按钮固定在段落后,而不是卡片模块的底部。通过给卡片模块添加display flex我们可以使用flex来控制图片和内容块。我们还会添加flex-direction column,因为我们想把它当作一个充满高度的栏目。

.flexbox .flex-card {
    display:flex;
    flex-direction:column;
}

第6步:卡片内容

我们还需要给一个项目加上display:flex,即卡片内容div,这样我们就可以控制它的元素,把它的方向改为纵向来让它知道我们想使用管状的内容并且添加flex:1 0 auto(thw grow is the important part)来让它填充满余下空间。

.flexbox .flex-card-content {
    display:flex;
    flex:1 0 auto; /* We have to add a basis for IE10/11 */
    flex-direction:column;
}

第7步:让段落Flexible

是否让内容块中的部分flexible取决于你,我会使用易变的项目,段落,来充满(添加flex:1 0 auto)。这会延伸段落底部并把按钮压到卡片底部。

.flexbox .flex-card-content p {
    flex:1 0 auto; /* We have to add a basis for IE10/11 */
}

flexbox如何让它更美好

有一系列方法来实现分屏布局-可以再次依赖栅格行,可以给每个项目最小高度,或者使用绝对定位的方法把项目钩在各自的方向上(left:0, bottom:0, top:0等)。或者我们可以固定高度为100vh,宽度为50vw并且忽略有多少内容并在外面包上一层div把这个div在盒子里居中。

所有这些方法的问题在于我们都需要控制高度。像之前的布局问题一样-设置最小高度在不同尺寸屏幕间是个问题(虽然当布局为一列时不是算是问题)。

添加flexbox解决了这个问题并提供我们一些工具来对齐容器内所有内容,还允许内容控制容器的尺寸。可以按需要垂直或水平对齐-包括垂直居中。

如果想重排子元素,我们也可以不用在DOM上移动项目来实现。

注意:当你使用排序时键盘控制可能是个问题,所以我说要保守使用,当然在较大的项目上不会产生许多不和谐的体验反而是较小的……。考虑键盘用户。

第1步:标签

这个实验的结构是一个div(父元素),内部两个div(子元素),如果超过两个我认为是无序列表-但只要两个就足够了。

在每个按钮面板里有图片,标题,段落和链接,我们将要用Flexbox对齐它们。

<!-- Split layout parent -->
<div class="flex-split">
    <!-- Child divs -->
    <div class="flex-split-left">
        <!-- Putting the image in a div here is totally optional -->
        <div class="flex-split-image">
            <img src="jake.png" />
        </div>
        <h3>Jake the dog</h3>
        <p>Etc etc etc</p>
        <a href="link.html">Read more</a>
    </div>
    <div class="flex-split-right">
        <!-- Etc, etc etc -->
    </div>
</div>

第2步:父div

对于没有flexbox的布局我使用一个最小VH高度(这在IE8中无效,如果想兼容IE8那就需要设置IE8中有效的高度单位)。对于浮动布局,需要更细致得管理屏幕变窄时的媒体查询来处理内容。我们也需要position:relative,这样可以给子元素绝对定位。

对于flexbox我们添加flex,一旦指定flex我们就可以用Flexbox操纵子元素。但这里不需要详细管理媒体查询因为当屏幕变窄时Flexbox会改变容器。

/* No flexbox layout with position relative and a minimum height */
.no-flexbox .flex-split {
    position:relative;
    min-height:80vh;
}
/* Adding Flexbox, switch to rows and wrap at 48em */
.flexbox .flex-split {
    display:flex;
    flex-direction:column;
}
@media all and (min-width:48em) {
    .flexbox .flex-split {
        flex-flow:row wrap;
    }
}

第3步:子元素

对于没有flexbox的布局我们浮动两个子div,指定宽度为50%,绝对定位固定在各自一边。

对于有flexbox的布局我们只需要添加flex-basis50%,选取一个在小屏幕上崩溃的点指定flex-basis100%(如果在小屏幕上不需要flexbox可以让它显示成堆叠的块元素)。

这里要把flex-direction改为column来排列成垂直内容。(虽然也可以用rowwrap,但这似乎需要更多代码所以我坚持用column)。我们给这个元素也添加上display:flex因为我们想用Flexbox来定位每个内容元素。

/* 
    For no flexbox - give the child elements a minimum height and position them top and bottom 
    at 48ems make them float, otherwise just let the content fill the blocks on a small screen
*/
@media all and (min-width:48em) {
    /* Common to both */
    .flex-split > div {
        width:50%;
    }
    .no-flexbox .flex-split > div {
        min-height:50vh;
        top:0;
        bottom:0;
        float:left;
        position:absolute;
    }
    /* Anchor to their respective sides  */
    .no-flexbox .flex-split-left {
        left:0;
    }
    .no-flexbox .flex-split-right {
        right:0;
    }
}
/* For Flexbox - make the direct descendant divs content displayed in columns */
.flexbox .flex-split > div {
    display:flex;   
    flex-direction:column;
}

第4步:用Flexbox对齐内容

如果像我一样你认为这时候指定align-item: center可以让内容垂直居中,那就错了。当改变Flexbox工作的轴时,你用在垂直轴上的属性改变了。现在有用的属性是justify-content

要居中子div中的内容,只要添加justify-content:center,如果还想能够重排,或者用任何觉得合适的方式对齐。

/* Use flexbox to vertically center align */
.flex-split > div {
    justify-content:center;
        /* I needed to do this in IE10/11 to get it to horizontally center */
    align-items:center; 
}

Flexbox疑难点

轴方向(flex direction)

当对齐不发生任何作用时,很可能因为你把轴的flex-directionrow换成了columnalign-itemsjustify-content也随着flex-direction发生旋转。我给所有东西加上边框后才发现了这个问题。

Flex项目水平排列

水平排列

当项目垂直方向超出页面,需要添加flex-wrap:wrap,或者又变成了响应式图像问题。

我开始用Flexbox时会添加前缀认为这样即便不支持Flexbox也没问题,直到测试老安卓设备时才意识到即便是支持Flexbox的设备,并一定支持Flex-wrap-这是我遇见这个问题的另一个理由。

非自适应图片

实际使用的过程中我碰到的一个问题是在flexbox容器中使用自适应图片。这是我最抱怨的事情-因为好像在每个浏览器中都发生。

当你设置某行的flex值为例如flex:0 0 25%时可能会发现这个问题,由于某些元素每3个项目会换行而不是4个。甚至更糟糕的是项目会突然跑出页面。因为图片无法收缩。添加flex-shrink也没有什么帮助。

问题在于Flexbox似乎重视最大宽度的最大值,它不认为maximun-width:100%的图片是可以伸缩的并且可以缩小尺寸。这不完全正确。这不是因为它不识别图片是可以伸缩的。真正原因是块级元素由内部图片尺寸决定。flex元素会试图调整自身到最大图片的尺寸。

解决方法

这个问题的解决方法是既不是设置最大宽度为一个你认为合适的非百分比单位。也不是完全伸缩-设为100%,显然背景有可能变的太大。

理解space-around和space-between

我希望我的实验把它弄清楚了一点,它是困扰我的一点。它们都自动计算,所以如果想要自己控制就不要用它们。space-around从中心固定项目并在flex容器中每个项目周围分配平均的空间。所以如果有两个项目,它们互相之间的边之间距离相等。可以想象成由内而外。

space-between让项目固定在容器外,计算让每个项目间距相等,把它想象成由外而内。如果想要实现固定paddingmargin,那么不要用space-between,你应该用flex-start

grow和shrink有什么用

这是更基本的错误,我知道大部分人不会遇到-如果不想让flex项变的过大,不要对它们使用grow。如果你只想扩大每一行最后一个项那么nth-child可以帮你更好的组织。

我的基本规则时:当我想要让某样东西填充一个消失项目的空间时我才用grow,当我想要某项倒塌腾出空间给新项目时才使用shrink

项目变为许多小列

另一个熟悉常见的情况,没有row-wrap的行。既不会让容器垂直排列,也不会让行有row-wrap

只有IE 10/11:过早换行,项目太大

非常感谢有一个Flex-bugs库,能帮助我们快速调试Flexbox。这主要是由于flex-basispadding不能很好的结合在一起。Flexbox不会接受border-box的盒模型解析,所以为了避免这种情况,使用flex-basisauto值,让其达到一个标准的宽度。

只有IE10/11:高度奇怪坍塌

当我创建卡片图案时,我发现按钮塌陷到段落中-这是flexbox中让我痛苦的另一个bug。感觉是有flex:1,但是没有生效。要解决这个问题请上完整的Flex:1 0 autoflex:1 0 0

FF:column-reverse和overflow-y

感谢Sean Curtis帮我发现了这个问题,最后发现这已经被记录为Mozilla的一个bug。如果在使用overflow-y:auto的元素上使用flex-direction:column-reverse,你根本不会看到滚动条。似乎这正在解决过程中。

整理好的跨浏览器bug

如果你遇到了这里没有列举的bug,也许它很有价值请查看Philip Walton的常见跨浏览器flexbox bug上面指出了一串他遇到的针对特定浏览器的bug。或者查看Git repo列出了我在之前提到的两个bug。我不断与列表上的4,5,7号bug斗争。

给布局添加flexbox

我已经见了人们添加Flexbox来改进他们的布局,我也在生产项目上使用Flexbox并且不像过去那样害怕。我小部分使用,测试小规模模型然后用我的方式使用。这个策略让我确保在不支持的浏览器上面我没有毁坏页面。

在成功使用的过程中有一个良好的策略来接触和实施Flexbox是至关重要的。没有一个清晰的策略,直接用Flexbox之后的工作像是在给自己挖坑。

如果你对Flexbox还没有信息,我建议试着用它重构已有的内容-看一看一些画廊式页面有趣的布局例如DribbblePattern Tap,或Awwwards并且看看Flexbox如何帮助改进它们。在最初几个小时不断用头敲打键盘后,你会发现其实挺容易的。希望我这里写的东西可以把你从满脸键盘印中拯救出来。

有没有任何用到Flexbox的新应用可以帮你从标准布局中改进的呢?或者你是否准备好在网页中用Flexbox?让我知道吧!我对人们用它正在做些什么很感兴趣。

延伸阅读

工具

  • Grunt autoprefixer - A handy tool for prefixing, also available are a Gulp version
  • Modernizr - Best damn feature detection library on the web
  • Too Flexy - a bookmarklet for switching between Flexbox and No-flexbox classes with Modernizr

本文根据@Chris Wright的《Using Flexbox today》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:http://chriswrightdesign.com/experiments/using-flexbox-today

Blueed

现居上海。正在学习前端的道路上,欢迎交流。个人博客:Blueed.me,微博:@Ivan_z3

如需转载,烦请注明出处:http://www.w3cplus.com/css3/using-flexbox-today.htmlAir Jordan 33 XXXIII

返回顶部