双飞翼布局和BFC

双飞翼布局和BFC

之前看到了一些面试题中,面试官会问到如果实现双飞翼布局或者是圣杯布局,这两个布局的理念基本是类似的,也就是中间的元素自适应,而左右两边的元素实际上是固定宽度的,也就是当减少页面宽度的时候,两侧的元素的宽度不变,而中间元素宽度减小。

而这个布局又要求中间的元素首先渲染,这样,在HTML中,元素的排版顺序应该是这样的:center,left,right,这样才可以保证中间元素首先渲染。

而为了实现双飞翼布局,那么需要用到margin和BFC相关的理论。

双飞翼布局的实现

双飞翼布局的HTML代码:

<body>
  <div class="container">    
    <div class="center"></div>    
    <div class="left"></div>    
    <div class="right"></div>  
  </div> 
</body>

flex

最简单的实现,也是在未来全局最优的实现方法肯定是CSS3提供的flex布局方式。弹性布局就是为了实现这样的自适应要求而生的。
使用flex布局实现双飞翼效果的重点有下面几个:

  1. 因为元素在DOM中的顺序是center -> left -> right,所以需要在渲染的时候对排列的顺序进行修改。

  2. 左右侧固定宽度。

  3. 中间的元素需要自适应。

综合上面三点,可以用下面的CSS来实现双飞翼布局。

.container {
  display: flex;
  height: 200px;
  /* 主轴上的对齐方式,由于左右要紧贴viewport的边缘,所以使用两端对齐的方式 */
  justify-content: space-between;
}
.left,
.right {
  height: 200px;
  width: 200px;
  background: lightblue;
  /** 由于左右两侧的元素宽度是固定的,
    * 所以这两个元素的缩小比例应该设置为0,表示不进行缩小
    */
  flex-shrink: 0;
}
/** 使用flex布局的order属性,来对元素的顺序进行重新排列,是原本的第一个元素center
  * 排序到第二位。
  */
.center {
  height: 200px;
  order: 2;
}
.left {
  order: 1;
}
.right {
  order: 3;
}

flex布局的优点在于对未来版本的兼容性,并且flex布局本身就是为了实现弹性布局,对于一些其他的需求也可以进行很好的扩展。

但是其现在对于浏览器的支持率还是有些差,具体支持情况可以看flex布局的浏览器支持情况—from caniuse

使用浮动

目前使用的最多的方法还是利用BFC和负margin进行的。对于margin这个属性的一些细微内容的说明可以见CSS margin相关

这个方法的思路是这样的:

首先需要将这些块级元素放到同一行中,为了保证不对其内部的效果产生比较大的影响,还是使用float来对元素进行浮动,而不是使用inline-block

由于center元素需要自适应,那么其宽度应该是按照比例进行的,那么左右两侧的元素需要固定宽度,所以center元素只能够使用100%宽度,然后想办法将leftright元素位移到上一行。

下面这些代码是毫无疑问的,也是双飞翼布局的基本代码:

.container {
  height: 200px;
  width: 100%;
}
.center {
  height: 200px;
  width: 100%;
  float: left;
}
.left,
.right {
  height: 200px;
  width: 200px;
  background: lightcoral;
  float: left;
}

现在得到的效果是这样的,需要将两个元素移到自已应该在的位置。

初始化的双飞翼布局

将元素进行位移的方法有很多,主要使用的是三种,transform: translate();,使用position进行定位,使用负margin

首先第一个,transform根本上还是对元素进行变换的,也就是说,它不会改变元素在文档流中的位置,这就意味着它还是会占据本来的位置,pass。

先说明后两个方法都是可以实现这个效果的。

使用position偏移

position可以为父元素添加relative属性值,然后为子元素添加absolute属性值,让三个子元素根据父元素进行定位。然后为center元素添加一个固定的padding,来为leftright腾出位置,这样就实现了双飞翼布局。

.container {
  position: relative;
}
.center {
  padding: 0 200px;
  box-sizing: border-box;
}
.left {
  position: absolute;
}
.right {
  position: absolute;
  right: 0;
}

上面的代码和之前的代码结合就可以看出来效果了。重点在于设置center元素的盒子模型,因为center元素本身的宽度需要占据整个父元素来实现自适应,我们知道现代浏览器的盒子模型都是content-box,也就是width属性值表示内容的宽度,这样导致了设置padding之后,center元素会溢出viewport。将其设置为border-box,也就是将paddingborder都包含在了width的宽度中。并且为左右侧元素腾出了空间。

然后使用根据父元素定位,将左右侧元素偏移到自己的位置,这个没有什么问题。Orz

使用负margin偏移

margin设置为负值,也可以将元素从文档流中偏移其本身的位置。

当然这个效果也需要使用border-box来让center元素给“左右护法”腾地方。

.center {
  padding: 0 200px;
  box-sizing: border-box;
}
.left {
  margin-left: -100%;
}
.right {
  margin-left: -200px;
}

这里要注意下,为什么右侧元素只需要偏移200px就可以上去了,因为左侧元素首先偏移了整个横轴,被放到了文档流的前面,那么右侧元素在文档流的位置就是原本左侧元素的位置,所以只需要偏移200px。

但是,但是,如果左侧元素不进行偏移,那么右侧元素无论如何设置margin-left,都是不能够向上偏移的。

重点在于每个浮动元素都会生成一个新的BFC,有着自己的上下文,而每个浮动的BFC之间是挨个排列的,如果其左侧元素没有偏移到上一行,那么其负margin进行的偏移仍旧是在当前行中进行的,如果仅仅对右侧元素进行-400px的偏移,那么该元素会直接溢出,而不是被偏移到上一行中。

使用calc进行直接计算

最最简单,也是当前浏览器支持率最低的实现方法,可以使用CSS新标准提供的calc函数来直接计算center元素的宽度,这样连border-box都不需要设置了。

这个方法也需要margin的一点小小的帮助来hack。

.center {
  width: calc(100% - 200px);
  margin-left: 200px;
}
.left {
  margin-left: calc(200px - 100%);
}

三行CSS属性就可以得到需要的效果了。当然这些代码都需要和上面的基本代码组合起来。

结果

上面的这些方法的组合或者是单独的方法实现的效果都是一致的,就是文章开头提到的DOM顺序为center、left、right,显示顺序为left、center、right。center元素自适应,left、right元素固定宽度。
效果如下:

双飞翼布局的效果

这些方法都有着自己的优点和缺点,需要根据自身使用情况来决定使用哪种方法来实现。

BFC

提到了浮动,就需要对BFC进行一些简单的解释。
浏览器对于DOM树的渲染是基于CSS样式规则来生成render树的,每个render树的节点都是一个矩形的元素,这些元素被按照一定顺序排列到viewport中,然后来进行绘制的。

那么这些矩形的元素之间的关系是什么样的呢,这些矩形的元素进行渲染的时候有相应的渲染规则,这个规则就是上下文。

每个矩形元素根据其样式的不同,会被分配到不同的类型,这个类型叫做Formatting Context
。BFC中的B代表着这个上下文是块级元素的上下文,所以BFC的全称应该是Block-level Formatting Context

块级上下文是根据一些CSS定义的:

  • 根元素。

  • float属性不为none,也就是浮动元素。

  • positionabsolute或者fixed,也就是脱离文档流的元素。

  • overflow不为visible,也就是内容不会溢出。

这些元素被按照该上下文进行渲染,生成严格的矩形。

BFC的渲染规则

  • BFC内部的元素按照垂直顺序,一个接一个的排列。
  • BFC内部的元素的border-box和BFC本身的左侧margin相接。
  • BFC的区域在自然情况下不会和其他的BFC重叠。
  • BFC是一个独立的渲染容器,其内部的元素不会影响到外面的元素。
  • BFC计算高度的时候,浮动元素也会参与计算。

使用

清除浮动

看到上面渲染规则的最后一点,肯定很多人都会有一个大胆的想法,就是清除浮动,浮动的元素会在成其父元素不能够撑开高度,这是一个普遍的问题,在现在的CSS开发环境下,很多时候都会使用浮动来对元素进行布局。清除浮动也是一个比较关键的技术。

还是用上面的双飞翼布局的三个元素进行测试:

.container {
  width: 100%;
  background: lightblue;
}
.center,
.left,
.right {
  height: 200px;
  float: left;
}
.center {
  width: 100%;
}
.left,
.right {
  background: lightcoral;
}

这时候,父元素的高度不会自动被子元素撑开,需要手动设置父元素高度才可以,下面的图中可以看到父元素的高度为0,并且父元素的背景色也没有被渲染出来:

浮动导致的父元素不能够撑开高度

为了自适应子元素高度,让父元素自动被撑开高度,基本上常用的方法是给父元素添加一个伪元素,然后对伪元素应用clear: both;来清除浮动的副作用,使用BFC有一个简单的办法,直接将父元素设置为BFC,这样根据上面的:

BFC计算高度的时候,其内部的浮动元素也会参与计算。

父元素的高度也可以被撑开。生成一个BFC的方法有很多种,可以根据自己的需求选择一个:
如果父元素本身也需要浮动,那么其本身就会变成BFC。

如果父元素需要定位,那么可以借用absolute或者fixed的副作用。

如果需要side-effect最小,那么可以设置其overflow

下面的几种方法都可以达到清除浮动的效果:

/* 方法1:常用方法,伪元素配合clear: both */
.container::after {
  content: '';
  display: block;
  clear: both;
}
/* 方法2:减小副作用 */
.container {
  overflow: hidden;
}
/* 方法3:设置父元素浮动 */
.container {
  float: left;
}

基本的伪元素方法有一个小缺点在于,当前环境下CSS的伪元素作用很大,而且伪元素只有两个,用一个少一个,还算比较珍惜的东西,用来清除浮动稍微有点浪费,但是好处在于这个方法基本没有任何的side-effect。

两侧布局

如果有两个元素:

<div class="left">  
</div>
<div class="main">
</div>

我们需要将left元素放到左边,并且main元素放到右边,并且main元素宽度自适应,如果不考虑一些比较麻烦的方法,我们会考虑的应该是给left元素添加左侧浮动。

两侧布局初始

得到的结果是这样的,main元素被left元素覆盖了一部分,如果想要main元素可以只占据自己的那一侧:

BFC在自然情况下不会和其他BFC重叠。

左侧的float自然是一个BFC,那么将右侧main元素变成BFC的方法也就有很多了,都是一样的效果。
但是这里不能对右侧元素使用浮动,因为其并未指定宽度,使用浮动会导致其无法撑开。这里使用:

.main {  
  overflow: hidden; 
}

就可以实现这样的效果了:

两侧布局效果

结论

BFC理论初次看来对于我们可能没有什么帮助,但是细细深究下去,我们平常的很多实现都是依据标准中的BFC来进行的。即使不知道在使用,也是离不开的,对于其深入的了解可以让我们在使用中找到很多的hack方法,并且更好的理解浏览器对于页面的绘制过程。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,123评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,031评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,723评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,357评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,412评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,760评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,904评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,672评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,118评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,456评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,599评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,264评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,857评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,731评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,956评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,286评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,465评论 2 348

推荐阅读更多精彩内容

  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,734评论 1 92
  • 一,浮动元素有什么特征?对父容器、其他浮动元素、普通元素、文字分别有什么影响? 浮动模型是一种可视化格式模型,浮动...
    DeeJay_Y阅读 862评论 0 4
  • relative:生成相对定位的元素,通过top,bottom,left,right的位置相对于其正常位置进行定位...
    zx9426阅读 932评论 0 2
  • 浮动定位 BFC 边距合并 浮动元素 div的顺序是HTML代码中div的顺序决定的。 浮动可以理解为让某个div...
    nianxiaoge阅读 711评论 0 0
  • 1、破相对容易,立何其艰难。 2、这个世界太可怕,比自己牛很多倍的人比自己还要努力很多倍。关键是自己还常常视而不见...
    长征阅读 244评论 0 0