CSS中关于定位及BFC中的易错点

1,579 阅读8分钟

说起BFC,就必须先了解一下CSS文档流中的定位机制,而且这部分说简单也简单,但却有个坑有可能误导我们,特在本文作出解释。

一、文档流中的定位机制

1. 三种基本方式

CSS有三种基本定位机制,我想大多数朋友都掌握了,在此简要概述一下(不浪费时间):

  1. 普通流(或称常规流):CSS默认的定位方式,触发方式包括为position: static/relative,且float:none
  2. 浮动:浮动脱离普通流,可以左右移动,直到它的外边框边缘碰到包含框或另一个浮动框的边缘,触发方式基本就是float:left/top等。
  3. 绝对定位:盒子脱离普通流,不影响普通流上其他元素的布局

2. 注意事项

所以,此时大部分初学者都会结合现实世界感觉到这三种流的立体感就是如下图所示的这样:

image

这里我想说的是,上图这种表达方式不能说是完全正确,虽然层级明确了,但这会给初学者带来错误的理解:浮动元素既然已经玩飘了,且普通流中的div也不会再给它留地方了,那浮动元素对普通应该就彻底没影响了吧? 当然不是! 直接上个小例子看看(多余上色之类的代码去掉了):

<body>
  <div class='parent'>
    <div class='child1'>
       Child1
    </div>
	<div class='child2'>
	    冷咖啡离开了杯垫
		我忍住的情绪在很后面
	</div>
  </div>
</body>
<style>
.parent {
  width:400px;
  min-height:100px;
}
.child1 {
  width:50px;
  height:50px;
}
.child2 {
  width:100px;
  height:100px;
}
</style>

效果如下:

image

好的,那接下来,按照我们的想法,把child1设为浮动,此时它就应该脱离普通流,且普通流中的元素不再考虑它的位置了,child1添加float:left后如下:

image

咦!child1确实感觉浮动了,child2也确实毫不客气的把child1原本的位置给顶替了, 可child2里面的元素是咋回事?为什么像躲摔倒的老太太一样躲避child1了呢? 这就是我想提醒朋友们一定要注意的地方。下面看看解释:

float起初被设计出来的初衷其实是为了实现报纸上的那种文字环绕图片的效果,就像我们上例这种普通流中的文字环绕在child1的周围。但之后大家才发现结合float + div 可以实现一定的网页布局,所以,这是浮动的特性。即浮动只是脱离了文档流,即不会按照你的设计而进行布局,就算它在任何位置上那也是和文档流中的其他内容是是平级关系的,我们在使用浮动时,基本上就考虑它的布局定位就行了,不要觉得它彻底飘了,它的影响还在。

好了,这个坑帮不知道的朋友解决了,接下来就好理解BFC了。

二、BFC探索之路

你可能在不知道BFC是啥玩意的前提下却知道解决子div浮动而造成父div塌陷的一个经典的办法就是给父div设置overflow:hidden,但要是面试官问你为啥,你可能就歇菜了,别问我怎么知道,我曾经就是那个菜鸡...,所以,了解了BFC,你就知道为啥了。

1. BFC概念

BFC(Block Formatting Context,块格式上下文)是Web页面的可视化CSS渲染的一部分,并且有自身的一套渲染规则,它决定了其子元素如何定位,以及和其他元素的关系和相互作用。

2. BFC特点

具有BFC特性的元素可以看成是隔离了的独立容器,容器里面的元素不会在布局上影响到外面的元素,并且BFC具有普通容器所没有的一些特性。很多好的文章罗列的内容有:

  1. 内部的box会在垂直方向,从顶部开始一个接一个地放置;
  2. box垂直方向的距离由margin决定,属于同一个BFC的两个相邻的box的垂直方向的margin会发生叠加;
  3. 在BFC中,每个盒子的左外边缘(margin-left)会触碰到容器的左边缘(border-left)。对于从右到左的格式来说,则触碰到右边缘,即使是浮动的也是如此,即不会发生margin穿透;
  4. 形成了BFC的区域不会与float box重叠;
  5. 计算BFC高度时,子浮动元素也参与计算(BFC会确切包含浮动的子元素,即闭合浮动);

【补充 2019/04/10】 针对2的内容,有朋友提出了,普通布局流中,同一个父box(不论是不是BFC)的两个相邻的box元素的垂直方向任然也会发生重叠。是的,但如果父box是个BFC,那它存在如下特点: (1) 普通流中,如果父box的直属子box设定了垂直margin,则它并不会撑大父元素,效果如下:

上图中,设定两个子元素p为margin:20px 0,可以看到p之间有重叠,但它们和父box元素的间距是0;

(2)针对上述问题,如果把父box设为BFC(给一个overflow:hidden),则效果如下:

这下父box就被撑开啦,但此时子box之间的垂直方向的margin仍然是重叠的。

所以,再遇到下面这些问题时,就可以回答了:

  1. 如何保证一个div元素不被同级的浮动元素覆盖:设置该div元素为BFC;
  2. 为什么父div设置overflow:hidden就可以在其子div为浮动的情况下依然可以撑开:因为此时父div是个BFC。

但是!这里我还想说一下我的一些想法,即:我认为,之所以提出BFC的概念,其主要目的是为了隔离出独立容器,而容器本身与其他元素之间是否完全满足上述特性,这应该区别对待,我们应该把BFC的核心思维用在它本身内部而非外部,后面文章会有例子。

3. 触发BFC的条件

有朋友罗列了好多,记住一些常规的即可:

  1. 根元素或包含根元素的元素,这里应该就是body元素;
  2. 浮动元素(float不是none);
  3. 绝对定位元素(position:absolute/fixed);
  4. 行内块元素(display:inline-block);
  5. 表格单元格(display:table-cell ,html表格单元格默认为该值);
  6. 表格标题(display:table-caption , html表格标题默认为该值);
  7. 匿名表格单元格元素(display:table / table-row / table-row-group / table-header-group / table-footer-group / inline-table ,分别是html table 、row、tbody、thead、tfoot的默认属性);
  8. overflow:非visible 的块元素;
  9. display:flow-root;
  10. contain:layout / content / strict;
  11. 弹性元素(display:flex / inline-flex 元素的直接子元素);
  12. 网格元素(display:grid / inline-grid 元素的直接子元素);
  13. 多列容器(元素的column-count 或 column-width 不为 auto,且包括 column-count为1);
  14. column-span 为all 的元素始终会创建一个新的BFC,即使该元素没有报过在一个多列容器中;

所以,每次看到 overflow:hidden、float:left/right 、position:absolute/fixed ,不用怀疑,一定是BFC

4. 实际使用场景

BFC在实际开发布局中基本可以做这两件事:

  1. 避免由于子元素浮动造成父元素塌陷;
  2. 避免同一个BFC中的两个块级元素的垂直方向margin重叠(设置其中一个为一个新的BFC,永久隔绝外部影响);
  3. 自适应两栏布局。

5. BFC实例

直接看看吧

<body>
    <div class='box'>
	   <div class='left'>左边</div>
	   <div class='right'>右边
		 <div class='little'>1</div>
		 <div class='little'>2</div>
		 <div class='little'>3</div>
	   </div>
	</div>
</body>
<style>
.box {
  background:#888;
  overflow:hidden;
  margin-left:50px;
}
.left {
  background: #73DE80;    /* 绿色 */
  width:200px;
  height:200px;
}
.right {
  background: #EF5BE2; /* 粉色 */
  width:400px;
  min-height:100px;
}
.little {
    background: #fff;
	width: 50px;
	height: 50px;
	margin: 10px;
	float:left;
}
</style>

效果如下:

image

如果突然把div left元素 添加一个float:left:

image

看到了吧!div right元素的子元素腾地方了,那接下来把div right变成一个BFC,比如使用:overflow:hidden,效果如下:

image

这样,就满足了BFC内部元素不受外界干扰,且不会与float元素重叠两个特性。

但是,如果我给box right 元素设置了绝对定位,那它是会覆盖box left这个浮动元素的,可这就不符合BFC的不会与float元素重叠这个特性了:

image

所以我提出了我和我同事讨论出的想法,即我们不能把上述罗列的BFC特性彻底肯定,因为这里涉及到的是两个大的知识层。当设置了绝对定位,虽然也触发了BFC,但是它更是直接影响了层级关系,所以优先实现后者。

以上就是我想提醒广大和我一样的初级朋友,欢迎大家批评指正,谢谢您!!!~~~

参考文献
  1. 《学习 BFC》 juejin.cn/post/684490…
  2. 《关于BFC理解》 reng99.cc/2018/08/12/…