跨浏览器伸缩布局进阶教程

http://dev.oupeng.com/articles/advanced-cross-browser-flexbox

 

CSS3 弹性布局模块 —— 短命名叫 Flexbox —— 为我们的 web 开发带来了许多能量和令人兴奋的畅想空间,我们可以快速轻松地搭建复杂的网站布局,摒弃一些我们惯常使用的不合理的 hacks 和 补丁。我的文章 《伸缩布局 — 打开布局天堂之门?》讲述了一些 Flexbox 的基础,本文将是进阶教程,我会带大家看一些高级点的例子,使用 Modernizr 为那些参差不齐的 flexbox 支持情况提供不同的样式,以便提供当前可用的最优的跨浏览器方案。

看下面例子

我为文章做的例子如图1:

图1:最终布局例子的截图

实例包含多层 flexbox 容器,比较急切的话可以先看看最终效果,然后继续探索代码细节。

总体布局

基本的网站布局像这样:


<section>
  <nav></nav>
  <article></article>
  <article></article>
</section>

<section> 被设置成一个弹性盒子像这样:


section {
  display: -ms-flexbox;
  -ms-box-orient: horizontal;

  display: -webkit-flex;
  display: -moz-flex;
  display: -ms-flex;
  display: flex;

  -webkit-flex-flow: row wrap;
  -moz-flex-flow: row wrap;
  -ms-flex-flow: row wrap;
  flex-flow: row wrap;
}

注意:与众不同的 IE 专有属性定义在 CSS 规则的最上面,因为当前 IE10 支持不同的 flexbox 语法(从2011年)直到最新标准被 Opera 和 Chrome 支持。更糟的是 Firefox 和其它的 WebKit 浏览器(像 Safari)支持老版本的语法(自2009年)。除此之外, Modernizr 指出 IE10 支持最新的 flexbox,实际不是,因此我们需要像这样兼容 IE10,而不是按 Modernizr 规则。见下文,flexbox 智能退化部分,有详细阐述。

文档流是横向的,但是强制 <nav> 标签单独占一行,用以下定义:


nav {
  padding: 1rem;
   -webkit-flex: 1 100%;
  -moz-flex: 1 100%;
  -ms-flex: 1 100%;
  flex: 1 100%; /*译者注:[ flex : 占父容器的比例 宽度基准值]*/
}

设置 flex-basis(伸缩基准值)为100%,让 nav 导航占据它的父容器宽度的100%,强制其它 flexbox 子元素换行。<article> 设置了 flex-grow 值,如下:


article:nth-of-type(1) {
  -webkit-flex: 2;
  -moz-flex: 2;
  -ms-flex: 2;
  flex: 2;
}

article:nth-of-type(2) {
  -webkit-flex: 3;
  -moz-flex: 3;
  -ms-flex: 3;
  flex: 3;
}

它们占一行的空间比例如此分配 —— 第一个 <article> 占40%,或宽度的 2/5,第二个 <article>(图片容器)占据60%,或者宽度的 3/5。切记 —— 只有这些项在同一行时,这个比例值才有效。

注意:定义的 flex-basis (伸缩基准值),首先会应用到 flexbox 子元素,之后,父容器剩下的空间会根据
flex-grow 比例值分配。像上面的 nav 的样式,flex-grow 没明确指定的话,默认会当成 1 。想学习更多的使用信息,可以读《伸缩布局 — 打开布局天堂之门?》的“让你的元素可伸缩”那部分。

Child flexboxes(子元素弹性盒子)

当你把一个元素设置成弹性的盒子,仅有它的直接子元素可伸缩,更深层的后代元素不会起作用。但是没有什么可阻止你让这些后代元素也可伸缩,来吧,做一个真正复杂的布局!我已把 <nav> 设置成弹性布局,因此不必担心它有多宽,我会轻松地让它居中:


nav {
    display: -ms-flexbox;
  -ms-box-orient: horizontal;
  -ms-box-pack: center;

  display: -webkit-flex;
  display: -moz-flex;
  display: -ms-flex;
  display: flex;

  -webkit-justify-content: center;
  -moz-justify-content: center;
  -ms-justify-content: center;
  justify-content: center; 
}

然后,我已把 <ul> 设置成弹性布局,被包裹的 <li> 子元素已居中:


nav ul {
  text-align: center;

    display: -ms-flexbox;
  -ms-box-orient: horizontal;
  -ms-box-pack: center;

  display: -webkit-flex;
  display: -moz-flex;
  display: -ms-flex;
  display: flex;
  -webkit-flex-flow: row wrap;
  -moz-flex-flow: row wrap;
  -ms-flex-flow: row wrap;
  flex-flow: row wrap;
  -webkit-justify-content: center;
  -moz-justify-content: center;
  -ms-justify-content: center;
  justify-content: center; 
  width: 80%;
}

nav a {
  width: 100%;
}

我还添加了一些其他属性来修饰导航各项的外观。我希望 <ul> 不完全拉伸到整个屏幕,所有文本都居中,因此我设置 width : 80%text-align : center
我也要确保 <a> 锚点占据 <li> 的整个宽度,设置为 width : 100%

接下来是见证奇迹的时刻,列表项看起来有点重叠,当它们折行的时候看起来很丑。无需 media queries (媒体查询)打造一个帅气的响应式菜单如何?仅需以下的 CSS 定义:


nav ul li {
  margin: 0 1.5rem;

  -webkit-flex: auto;
  -moz-flex: auto;
  -ms-flex: auto;
  flex: auto;

  min-width: 5rem;
}

我用 margin 给列表项留了一些呼吸空间(译者注:设置了左右间距),定义了 min-width,设置 flexautoautoflex 很特别的值,它允许 flex 子元素根据已定义的 min-width 值平均分配可用空间,它会根据可用空间大小自动调节每个子元素的宽度。扩大和收缩此页面看看会发生什么(截图见图2)?


图2:神奇的响应式菜单!

我也把第二个 <article> 设置成弹性布局,像这样排列里面的 <p> 标签(每个包含一个图片):


article:nth-of-type(2) {
  display: -ms-flexbox;
  -ms-box-orient: horizontal;
  -ms-box-pack: center;
  -ms-box-align: center;

  display: -webkit-flex;
  display: -moz-flex;
  display: -ms-flex;
  display: inline-flex;

  -webkit-flex-flow: row wrap;
  -moz-flex-flow: row wrap;
  -ms-flex-flow: row wrap;
  flex-flow: row wrap;

  -webkit-justify-content: center;
  -moz-justify-content: center;
  -ms-justify-content: center;
  justify-content: center;

  -webkit-align-items: center;
  -moz-align-items: center;
  -ms-align-items: center;
  align-items: center;

  -webkit-align-content: flex-start;
  -moz-align-content: flex-start;
  -ms-align-content: flex-start;
  align-content: flex-start;
}

article p {
  margin: 0.5rem;

  -webkit-flex: 1 20rem;
  -moz-flex: 1 20rem;
  -ms-flex: 1 20rem;
  flex: 1 20rem;
}


article p img {
  display: block;
  width: 100%;
  border: 1px solid black;
}

我把它们设置成固定的 flex-basis (伸缩基准)值,因此它们可以根据页面宽度的增大减小,动态改变每行的图片数量(看图3 —— 又一次无需 media queries),每一项内容都会保持水平居中,空间小时会垂直一列。


    

图3:无需 media queries 的响应式图片盒子。

flexbox 智能退化

这些浏览器支持 flexbox (WebKit 浏览器,基于 Presto 的 Opera,Firefox 支持有限,不久会完善!),在 flexbox 成为主流之前,支持它的浏览器还是比较少。显然,如果我们想用到生产环境代码中,需要一些智能的替代品。一些浏览器(老的 WebKit 们,Firefox)支持一些 2009年的老版的 flexbox 语法。IE 10 从2011年开始支持奇怪的新旧组合。万幸的是,Modernizr 特性检测框架可以帮助我们检测最新的 flexbox 语法和遗留的 flexbox 语法,它是通过 flexbox flexbox-legacy 标记识别的。不过还有问题 —— Modernizr 反馈显示 IE 10 支持最新的 flexbox,实际还没支持 —— 它支持一种新旧之间的语法!这就是我们把 IE 10 专有的属性放到我们 CSS 代码里的原因。

注释:这是关于 Modernizr flexbox IE10 探测问题的讨论,截止到2013年4月议题还在进行中。

下面的表格是关于最新的 flexbox 语法,等价的2009年的语法,以及 2011年的混合语法的总结:

以上表格呈现了不同浏览器支持的不同的 flexbox 语法,以及不同属性等价的写法。
最终语法2009语法2011混合语法
display: flex display: box display: flexbox
flex-direction: row box-orient: horizontal box-orient: horizontal
justify-content: flex-start box-pack: start box-pack: start
align-items: flex-start box-align: start box-align: start
flex: 1 box-flex: 1 flex: 1
注意:2009年的规范里有 box-lines 属性,看起来像是 flex-wrap 的等价物,不幸的是支持老版语法的浏览器似乎不支持它。我怕例子无法正常运行,为老版浏览器做了简化。

因此我为不支持 flexbox 最新语法,但支持老版语法的浏览器写了退化样式:


/* 老版 flexbox 退化样式 */

.no-flexbox section {
  display: -webkit-box;
  display: -moz-box;

  -webkit-box-orient: horizontal;
  -moz-box-orient: horizontal;
}

.no-flexbox nav {
  padding: 1rem;
  width: 20%;
}

.no-flexbox article {
  -webkit-box-flex: 1;
  -moz-box-flex: 1;
}

.no-flexbox article p {
  float: left;
}

.no-flexbox article img {
  display: block;
  width: 200px;
}

如果连老版本的 flexbox 都不支持的话,我做了如下替代:


.no-flexbox-legacy nav, .no-flexbox-legacy article {
  float: left;
}

.no-flexbox-legacy nav {
  width: 20%;
}

.no-flexbox-legacy article {
  width: 36%;
}

.no-flexbox article img {
  float: left;
}

注意:一个生成跨浏览器的 Flexbox 代码,学习不同浏览器的语法差异的神器,就是它啦 Flexy Boxes

为太宽和太窄的屏幕加一点简单的 media queries 代码

最后,我还是决定用一点 media queries ,仅仅是为了修复过窄的屏幕宽度问题。但是请自己斟酌用多小的尺寸:flexbox 创建的布局与生俱来的灵活,会根据屏幕宽度变化。

首先,对老版的 flexbox 布局做了简单修复:


@media all and (max-width: 600px) {
  h1 {
    font-size: 5rem;
  }

  .no-flexbox section { 
    -webkit-box-orient: vertical;
    -moz-box-orient: vertical;
  }

  .no-flexbox nav {
    width: 100%;
    margin-left: -3rem;
  }

  .no-flexbox nav a, .no-flexbox nav ul, .no-flexbox nav li {
    width: 100%;
  }

}

然后,对支持最新 flexbox 和不支持 flexbox 的浏览器在小屏幕显示时做修复。


@media all and (max-width: 480px) {
  article:nth-of-type(1) {
    -webkit-flex: 1 100%;
    -moz-flex: 1 100%;
    -ms-flex: 1 100%;
    flex: 1 100%;
  }

  body {
    min-width: 320px;
  }

  nav ul {
    width: 100%;
  }

  .no-flexbox-legacy nav, .no-flexbox-legacy article {
    float: none;
  }

  .no-flexbox-legacy nav, .no-flexbox-legacy article {
    width: 100%;
  }
}

最后的 media queries 仅仅为了在很宽的屏幕上居中内容。


@media all and (min-width: 1100px) {
  section {
    width: 1100px;
    margin: 0 auto;
  }
}

结语

现在使用 flexbox 仍有一些限制,等主流浏览器都支持的时候就容易多了。如今,如果想保持简洁,保持一行用 flexbox 非常高效,在支持老 flexbox 语法的浏览器上,多行 flexbox 布局还不太好使。因为我的例子符合标准,所以它运行良好,尽管老版语法布局看起来没有最新 flexbox 语法布局那么帅;有个有意思的问题:用 flexbox 布局再用 margin : 0 auto 在 Firefox 貌似无法居中内容(在 Safari 运行良好)。

无论如何,对于简单的 flexbox 应用,在一大批主流浏览器上已经可以运行良好啦:Chrome, Firefox,Safari,Opera Presto 12.1+, IE 10+,iOS 和 Android。

posted on 2015-03-17 11:20  鬼鬼丫404  阅读(207)  评论(0编辑  收藏  举报

导航