新建BFC与hasLayout触发场景浅析—高度塌陷原因分析
Author:[email protected] Date:
原理就是 Block formatting contexts (块级格式化上下文),以下简称 BFC,它是CSS2.1规范定义的,是页面 CSS 视觉渲染的一部分,用于决定块盒子的布局及浮动相互影响范围的一个区域。简单来说,BFC 就是一种属性,这种属性会影响着元素的定位以及与其兄弟元素之间的相互作用。
产生新的BFC情形总结:
根元素或其它包含它的元素
浮动 (元素的 float 不为 none)
绝对定位元素 (元素的 position 为 absolute 或 fixed)
行内块 inline-blocks (元素的 display: inline-block)
表格单元格 (元素的 display: table-cell,HTML表格单元格默认属性)
表格标题 (元素的 display: table-caption, HTML表格标题默认属性)
overflow 的值不为 visible的元素
弹性盒 flex boxes (元素的 display: flex 或 inline-flex)
其中,最常见的就是overflow:hidden、float:left/right、position:absolute。也就是说,每次看到这些属性的时候,就代表了该元素已经创建了一个BFC了。
需要注意的是,display:table 本身并不会创建BFC,但是它会产生匿名框(anonymous boxes , 没有名字不能被选择器选中的盒,它们的所有属性都为inherit或初始默认值;),而匿名框中的display:table-cell可以创建新的BFC,换句话说,触发块级格式化上下文的是匿名框,而不是display:table。所以通过display:table和display:table-cell创建的BFC效果是不一样的。
BFC的特性
内部的盒会在垂直方向一个接一个排列(可以看作BFC中有一个的常规流);
处于同一个BFC中的元素相互影响,可能会发生外边距叠加,如果这两个相邻的块框不属于同一个块级格式化上下文,那么它们的外边距就不会叠加。;
每个元素的margin box的左边,与容器块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此;
BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之亦然;
计算BFC的高度时,考虑BFC所包含的所有元素,连浮动元素也参与计算;
浮动盒区域不叠加到BFC上;
BFC包含创建该上下文元素的所有子元素,但不包括创建了新BFC的子元素的内部元素,也就是一个元素不能同时存在于两个BFC中。
通俗地来说:创建了 BFC的元素就是一个独立的盒子,里面的子元素不会在布局上影响外面的元素,反之亦然,同时BFC仍然属于文档中的普通流。
那么到底为什么坍塌?
元素脱离了普通流,并且创建了新的BFC,而父元素不具备产生 BFC 的条件,所以它的高度为0。
BFC(主流浏览器)
通过了解BFC的特性我们知道,BFC会把它包含的浮动元素高度也算在里面,也就是闭合浮动。拿 overflow: auto 举例:overflow: auto 并不会闭合浮动,而是 overflow: auto 会创建一个新的BFC,避免浮动的元素侵入其他元素。
hasLayout(ie6/7)
IE6-7有一个特有的属性就是haslayout,当一个元素的hasLayout属性值为true时,我们说这个元素有一个布局(layout),它负责对自己和可能的后代元素进行尺寸计算和定位,当属性值为false时,它的尺寸和位置由最近拥有布局的祖先元素控制。
触发hasLayout的条件:
position: absolute
float: left|right
display: nline-block
width: 除 “auto” 外的任意值
height:除 “auto” 外的任意值 (例如很多人闭合浮动会用到height: 1% )
zoom:除 “normal” 外的任意值
writing-mode: tb-rl
很多情况下,把 hasLayout的状态改成true 可以解决很大部分ie下显示的bug。 hasLayout属性不能直接设定,通过设定一些特定的css属性来触发并改变 hasLayout 状态。
元素hasLayout而导致的问题其实一般都很容易发现:往往是内容出现错位甚至完全不可见。 如:当一个元素内含浮动或绝对定位的内容时,它通常会表现出奇怪和错误的行为
一般如果是因为layout而引起的显示不符期望效果的话,在ff下会表现正常,而在ie下会出现错误。这个时候可以尝试触发父容器及其中的子容器的haslayout属性,通常可以通过加上zoom: 1;来调试。直到找到了产生问题的元素,再进行针对性的修正。最好的办法是对这个元素设置尺寸属性。但是,有时不便指定尺寸属性的情况下,就只能寻找替代方案了。对于ie7 ,最好的办法是设置最小高度属性为0;这个技术是无害的,因为0本来就是这个属性的初始值。而且没有必要对其他浏览器隐藏这个属性。而对于ie6和更早版本中触发一个元素hasLayout的方法是在overflow属性是visible的情况下设置这个元素的高度属性为1%,然后对其他浏览器隐藏这个设置。这种技术就是著名的Holly hack。
如何解决高度塌陷
在支持BFC的浏览器(IE8+,firefox,chrome,safari)通过创建新的BFC闭合浮动;
在不支持 BFC的浏览器 (IE6-7),通过触发 hasLayout 闭合浮动。
其它的,可以节选张鑫旭的
《CSS深入理解流体特性和BFC特性下多栏自适应布局》
二、块状元素的流体特性与自适应布局
流体特性
块状水平元素,如div
元素(下同),在默认情况下(非浮动、绝对定位等),水平方向会自动填满外部的容器;如果有margin-left/margin-right
, padding-left/padding-right
, border-left-width/border-right-width
等,实际内容区域会响应变窄。
感受下div
元素的流体特性:随着margin
, padding
, border
的出现,其可用宽度自动跟着减小,形成了自适应效果。就像放在容器中的水流一样,内容区域会随着margin
, padding
, border
的出现自动填满剩余空间,这就是块状元素的流体特性。
流体特性
下面,我们稍微做一个调整,div
距离容器左侧margin
150
像素,里面的图片同样100%
自适应内容区域。HTML如下:
.flow-box { width: 500px; background-color: #eee; overflow:auto; resize:horizontal; } .flow-content { margin-left: 150px; }
<div class="flow-box"> <div class="flow-content"><img src="mm1.jpg" width="100%" height="190"></div> </div>
图片右下角有两道斜杠,我们可以resize拉伸(现代浏览器,且非移动访问),会发现,左侧永远150像素留白,而图片随着容器宽度变化而自适应变化了。
此时,我们需要好好利用左侧150像素的留白间距,岂不是就可以实现两栏自适应效果!?
为了不影响原本的流体特性,我们可以使用破坏性属性,如浮动(float:left),或者绝对定位(position:absolute)。
我们直接HTML如下调整即可:
<div class="flow-box"> <img src="mm1.jpg" width="128" style="float:left;"> <div class="flow-content"><img src="mm1.jpg" width="100%" height="190"></div> </div>
<div class="flow-box"> <img src="mm1.jpg" width="128" style="position:absolute;"> <div class="flow-content"><img src="mm1.jpg" width="100%" height="190"></div> </div>
当然,你可以左侧有多个浮动,或者左浮动+右浮动。于是,我们不仅可以实现两栏自适应效果,多栏自适应效果也不在话下。
然而,利用块状元素流体特性实现的自适应布局有个不足,就是,我们需要知道浮动或绝对定位内容的尺寸。然后,流体内容才能有对应的margin
或padding
或border
值进行位置修正。于是,问题来了,我们没法单纯使用一个公用的类名,类似.clearfix
这样,整站通用。因为不同自适应场景的留白距离是不一样的。
此时,我们可以利用块状元素的BFC特定实现更强大更智能的多栏自适应布局(本文重点)。
三、元素的BFC特性与自适应布局
1. BFC元素简介与基本表现
BFC特性很多,而我们这里,只关心一个,和float
元素做相邻兄弟时候的表现。
如果是上面介绍的流体特性div
, 当其和浮动元素当兄弟的时候,是覆盖的关系(可以脑补下文字环绕图片效果)。但是,元素BFC化后,
会发现,普通流体元素BFC后,为了和浮动元素不产生任何交集,顺着浮动边缘形成自己的封闭上下文。如下截图:
同时,元素原本的流体特性依然保留了。哈,这个很重要,也就是,虽然不与浮动交集,自动退避浮动元素宽度的距离,但,本身作为普通元素的流动性依然存在,反映在布局上就是自动填满除去浮动内容以外的剩余空间。哟,这不就是自适应布局嘛!!
2. BFC自适应布局模块间的间距
然而,模块过于亲密接触,可能不是我们想要的。一般而言,我们需要一点间距。
说到间距,我们的第一反应就是margin
. 于是,我们给BFC元素增加一个margin-left:20px
, CSS代码如下:
.float-left { float: left; } .follow-content {margin-left: 20px;background-color: #cad5eb; overflow: hidden; }
结果……纳尼~ 怎么还是像狗屁膏药贴在一起啊??
您可以狠狠地点击这里:BFC元素增加一个margin无效demo
实际上,这里的margin并不是无效,而是值不够大,鞭长莫及啊!
用一个形象的Gif表示就是下面这样:
左侧浮动的图片就好比上面Gif图片中男孩的胳膊,妹子就是BFC元素,结果两人紧密接触。然后,margin-left
就是妹子的胳膊个脚,虽然也甩出去了,可惜长度有限,于是,毫无影响。
如果按照上面的解释,那我们把margin-left:20px
改成margin-left:150px
就应该有间距了? 一试便知!
.float-left { float: left; } .follow-content {margin-left: 150px;background-color: #cad5eb; overflow: hidden; }
结果,当当当当:
注意:我这里举margin
这个例子,不是让大家这样使用,只是为了让大家可以深入理解BFC元素与浮动元素混排的特性表现。实际开发,我们完全没有必要对BFC元素设置margin
, 因为又回到了流体布局,明明是固定的15像素间距,但是,每个布局都要写一个不同的margin
值,完全没有重用价值。
但是,间距部分的高潮来了!
我们可以使用浮动元素的margin-right
或者padding-right
轻松实现间距效果。间距是20
像素,直接:
.float-left { float: left;margin-right: 20px;}
与浮动元素的宽度是多少没有任何关系。不仅如此,我们还可以使用BFC元素的padding-left
撑开间距(虽然margin-left
作用鸡肋)。
于是,我们可能就会有:
.l { float: left; } .ovh { overflow: hidden; }
的自适应固定搭配。再配合zxx.lib.cssCSS样式库的margin
和padding
家族,快速布局可谓所向披靡。
3. 与纯流体特性布局的优势
BFC自适应布局优势我总结了下面2点:
自适应内容由于封闭,更健壮,容错性强。比方说,内部
clear:both
不会与兄弟float
产生矛盾。而纯流体布局,clear:both
会让后面内容无法和float
元素在一个水平上,产生布局问题。自适应内容自动填满浮动以为区域,无需关心浮动元素宽度,可以整站大规模应用。而纯流体布局,需要大小不确定的
margin
/padding
等值撑开合适间距,无法CSS组件化。
如下效果,图片能大能小,布局依然良好:
而CSS代码就是非常简单的:
.float-left {float: left; margin-right: 20px; } .bfc-content {overflow: hidden; background-color: #beceeb; }
可以说,有了BFC自适应布局,基本上没有了纯流体特性布局存在的价值。然而,只是理论上如此。如果,BFC自适应布局真那么吊炸天,那为何并没有口口相传呢?
那我们就得进一步深入理解了。
4. BFC元素家族与自适应布局面面观
理论上,任何BFC元素和浮动搞基的时候,都可以实现自动填充的自适应布局。
但是,由于绝大多数的触发BFC的属性自身有一些古怪的特性,所以,实际操作的时候,能兼顾流体特性和BFC特性来实现无敌自适应布局的属性并不多。下面我们牵驴遛马一个一个瞅瞅(类似行为仅出1个代表示意,你懂的,如float:left/right
):
float:left 浮动元素本身BFC化,然而浮动元素有破坏性和包裹性,失去了元素本身的流体自适应性,因此,无法用来实现自动填满容器的自适应布局。不过,其因兼容性还算良好,与堆积木这种现实认知匹配,上手简单,因此在旧时代被大肆使用,也就是常说的“浮动布局”,也算阴差阳错开创了自己的一套布局。
position:absolute 这个脱离文档流有些严重,过于清高,不跟普通小伙伴玩耍,我就不说什么了……
overflow:hidden 这个超棒的哦!不像浮动和绝对定位,玩得有点过。也就是溢出剪裁什么的,本身还是个很普通的元素。因此,块状元素的流体特性保存相当完好,附上BFC的独立区域特性,可谓如虎添翼,宇宙无敌!哈无诶瓦(However), 就跟清除浮动:
.clearfix { overflow: hidden; _zoom: 1; }
一样。由于很多场景我们是不能
overflow:hidden
的,因此,无法作为一个通用CSS类整站大规模使用。因此,float+overflow
的自适应布局,我们可以在局部(你确定不会有什么被剪裁的情况下)很happy地使用。display:inline-block CSS届最伟大的声明之一,但是,在这里,就有些捉襟见肘了。
display:inline-block
会让元素尺寸包裹收缩,完全就不是我们想要的block
水平的流动特性。唉,只能是一声叹气一枪毙掉的命!然而,峰回路转,世事难料。大家应该知道,IE6/IE7浏览器下,block
水平的元素设置display:inline-block
元素还是block
水平,也就是还是会自适应容器的可用宽度显示。于是,我们就阴差阳错得到一个比overflow:hidden
更牛逼的声明,即BFC特性加身,又流体特性保留。.float-left { float: left; } .bfc-content { display: inline-block; }
当然,
*zoom: 1
也是类似效果,不过只适用于低级的IE浏览器,如IE7~display:table-cell 让元素表现得像单元格一样,IE8+以上浏览器才支持。跟
display:inline-block
一样,会跟随内部元素的宽度显示,看样子也是不合适的命。但是,单元格有个非常神奇的特性,就是你宽度值设置地再大,大到西伯利亚,实际宽度也不会超过表格容器的宽度。因此,如果我们把
display:table-cell
这个BFC元素宽度设置很大,比方说3000像素。那其实就跟block
水平元素自动适应容器空间效果一模一样了。除非你的容器宽度超过3000像素,实际上,一般web页面不会有3000像素宽的模块的。所以,要是你实在不放心,设个9999
像素值好了!.float-left { float: left; } .bfc-content { display: table-cell; width: 9999px; }
看上去,好像还不错。但是,还是有两点制约,一是IE8+以上浏览器兼容,有些苦逼的团队还要管IE6;二是应付连续英文字符换行有些吃力(可以嵌套
table-layout:fixed
解决)。但是,总体来看,适用的场景要比overflow:hidden
广博很多。display:table-row 对
width
无感,无法自适应剩余容器空间。display:table-caption 一无是处……还有其他声明也都是一无是处,我就不全部展开了……
总结:我们对BFC声明家族大致过了一遍,能担任自适应布局重任的也就是:
overflow:auto/hidden
IE7+display:inline-block
IE6/IE7display:table-cell
IE8+
由于overflow有剪裁和出现滚动条等隐患,不适合作为整站通用类,于是,最后,类似清除浮动的通用类语句:
.clearfix { *zoom: 1; } .clearfix:after { content: ''; display: table; clear: both; }
两栏或多栏自适应布局的通用类语句是(block
水平标签,需配合浮动):
.cell { display: table-cell; width: 9999px; *display: inline-block; *width: auto; }
这就是zxx.lib.cssCSS样式库中.cell
的由来!
当然,由于和浮动元素合作,清除浮动还是要的,于是,就有了.fix
+ .l/.r
+ .cell
的无敌组合,可以多栏,也可以无限嵌套。
如果是局部,且确认安全;或有连续英文字符换行的隐患,你也可以使用.fix
+ .l/.r
+ .ovh
的无敌组合,可以多栏,也可以无限嵌套。
文章合集:
任务三——如何解决高度坍塌问题?——BFC模式 https://www.cnblogs.com/cjlalala/p/5556855.html
CSS深入理解流体特性和BFC特性下多栏自适应布局 http://www.zhangxinxu.com/wordpress/?p=458
转载本站文章《新建BFC与hasLayout触发场景浅析—高度塌陷原因分析》,
请注明出处:https://www.zhoulujun.cn/html/webfront/style/css3/2016_0224_7642.html