本文摘自: please call me HR
双飞翼又叫做圣杯布局。简单的说,双飞翼是中国叫法,圣杯是老外叫的。这是由: Matthew Levine 提出的。简而言之有几点注意事项:
中间一栏最先渲染
允许任意一栏放最上面
只需一个额外 div 标签
少用 HACK
实际上,强制要求就是前 3 个。他给的实现方式已经有了,我就简单的讲解一下他的实现吧。
原始双飞翼实现
完整demo,可以参考: 双飞翼布局。
他的基本 HTML 为:
<div id="header">#header</div>
<div id="container">
<div id="center" class="column">#center</div>
<div id="left" class="column">#left</div>
<div id="right" class="column">#right</div>
</div>
<div id="footer">#footer</div>
没啥特殊的。这里就满足了任意一栏放上面的要求。因为 container 里面包括了圣杯布局的基本实现。使用 div 来包裹他,防止影响到其他元素。
主要还是 CSS (因为这里根本就没用 JS)
header 和 footer 随便大家怎么放了,就是用到了 clear:both
清楚浮动。我们来观摩 container 里面的内容。
#container {
padding-left: 200px;
padding-right: 150px;
}
#container .column {
height: 200px;
position: relative;
float: left;
}
#center {
background-color: #000;
width: 100%;
}
#left {
background-color: red;
width: 200px;
right: 200px;
margin-left: -100%;
}
#right {
background-color: blue;
width: 150px;
margin-right: -150px;
}
// IE6 的 HACK 可以不用管
* html #left { // 定位 右边 container 的盒模型位置
left: 150px;
}
最最重要的是前面两个。
#container {
padding-left: 200px; // 定义 #left 的宽度
padding-right: 150px; // 定义 #right 的宽度
}
#container .column {
height: 200px;
position: relative; // 设置相对布局,便于调整位置
float: left; // 让包裹元素脱离文档流
}
ok, 也就是说,#container 里面的属性,应该是至关整个页面布局的属性。这里,可以根据单一职责原则,再进行优化,将清除浮动这个 trick 让他本身自己实现。即:
#container::after{
display:block;
content:'';
clear:both;
}
后面,我就挑 left 来进行简单说明(这篇想说的是 flexbox)。
#left {
background-color: red;
width: 200px;
right: 200px;
margin-left: -100%;
}
主要属性就是 margin-left:-100%;
,用来将自己吃掉。那么此时,看起来他的宽度就为0,这就意味着,他可以上移了。对应于 right 就是:
#right {
background-color: blue;
width: 150px;
margin-right: -150px;
}
但,这有一个问题,right 怎么会浮动到最右边呢?感觉他应该是紧挨着 left。我们现在讲 margin-right 给忽略,会得到:
关键的秘密在于,使用 margin-left 是不会当前层的文档流,更直观的就是,他们是在一个 Layer 上。
right 跟着上移时,会根据上面那一行进行布局。so,他就会到右边去了。补充一下:
margin 为负值的 problem
没有设置 width: 使用 margin 负值,会向指定方向拖动
设置 width: 会让元素内部塌陷,原来的样子不变。也就是说,其他元素会覆盖到它。
基本内容就是这些,MDZZ,一个双飞翼能扯出这么多。。。怪不得很多公司都喜欢问。。。主要的考点就是,margin 负值,position,left,清除浮动。
不过,我们这里,不扯这些,我们就用 flexbox。纯原生,快速实现。
flexbox 实现
这里,再说一下要求吧:
中间一列自适应
左右永远固定
对比与 flex 实现方式来说,这简直 so easy。我直接上代码吧:
// HTML
<div id="header">#header</div>
<div id="container">
<div id="left" class="column">#left</div>
<div id="center" class="column">#center</div>
<div id="right" class="column">#right</div>
</div>
<div id="footer">#footer</div>
// CSS
body {
min-width: 550px;
}
#container{
display: flex;
justify-content: center;
align-items: flex-start;
}
.column{
height: 200px;
color:white;
}
#center{
flex-grow: 1;
background-color: black;
}
#left{
flex-basis: 200px;
background-color: red;
}
#right{
flex-basis: 200px;
background-color: blue;
}
里面主要用到的就是 justify-content 和 align-items 来进行布局设置。
具体可以参考: holy grail
那两者有没有什么差异呢? 查看了 timeLine 发现, flexbox 渲染的层数也是一层:
ok... 这当然说明不了什么,我们具体来看一下渲染完成时间就 ok:
flexbox: ~80ms
原始 css: ~120ms
ok, 这下你知道该用哪一个了吧。
当然 flex 实际上就为了各种各样比较 HACK 的布局方式实现的。比如,侧边栏布局,多列布局等等。
侧边栏布局,我就不多说了,关键是掌握 flexbox 的基本属性。直接看源码就 ok。
当然,flexbox 最适合做的就是多列布局,而且你多列的顺序可以随意切换,只要简单修改 order
就 ok。
flexbox 兼容处理
当然,说了这么多,兼容性肯定是个大问题,因为从第一版的 box 到现在 W3C 标准的 flex。中间经历了很多让人费解的阶段,那使用 flex 的时候,非要让你手动加上每一个时代的 flexbox,这估计就疯了。所以,编译 flex 的插件就出来了。我经常使用的就是 flexboxfixer。简单的说,他就是将以前的 flexbox 的tip 给加上。它源码里也很清楚的写明了 flexbox 怎样做映射。
var mappings = {
'display': {
valueMap: {
'box': 'flex',
'flexbox': 'flex',
'inline-box': 'inline-flex',
'inline-flexbox': 'inline-flex'
}
},
'box-align': {
newName: 'align-items',
valueMap: {
'start': 'flex-start',
'end': 'flex-end'
}
},
'flex-direction': {
valueMap: {
'lr': 'row',
'rl': 'row-reverse',
'tb': 'column',
'bt': 'column-reverse'
}
},
'box-pack': {
newName: 'justify-content',
valueMap: {
'start': 'flex-start',
'end': 'flex-end',
'justify': 'space-between'
}
},
'box-ordinal-group': {
newName: 'order',
valueMap: {}
},
'box-flex': {
newName: 'flex',
valueMap: {}
}
};
这里就不过多赘述了。用的话也很简单,直接 postcss([flexboxfixer]) 即可。放一个我常用的 gulp 配置:
gulp.task('css', () => {
var processors = [ //这里就是中间件
autoprefix({
browsers: ['last 3 Safari versions', "last 2 Explorer versions", 'last 2 Explorer versions', "iOS 5"],
cascade: true,
remove: true
}),
flexpost
];
return gulp.src(["xx/*.css"]) // 引入CSS
.pipe(postcss(processors)) // 处理相关 css
.pipe(gulp.dest('dev/styles'));
});
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。