清除浮动技巧总结

一、为何要清除浮动?
要解答这个问题,我们得先说说CSS中的定位机制:普通流,浮动,绝对定位 (当中"position:fixed" 是 "position:absolute" 的一个子类)。
1)普通流:非常多人或者文章称之为文档流或者普通文档流,事实上标准里根本就没有这个词。假设把文档流直译为英文就是 document flow ,但标准里仅仅有还有一个词,叫做普通流 (normal flow),或者称之为常规流。但似乎大家更习惯文档流的称呼,由于非常多中文翻译的书就是这么来的。比方《CSS Mastery》,英文原书中至始至终都仅仅有普通流 normal flow(普通流) 这一词,从来没出现过document flow (文档流)

2)浮动:浮动的框能够左右移动,直至它的外边缘遇到包括框或者还有一个浮动框的边缘。浮动框不属于文档中的普通流,当一个元素浮动之后,不会影响到块级框的布局而仅仅会影响内联框(一般是文本)的排列,文档中的普通流就会表现得和浮动框不存在一样,当浮动框高度超出包括框的时候,也就会出现包括框不会自己主动伸高来闭合浮动元素(“高度塌陷”现象)。顾名思义,就是漂浮于普通流之上,像浮云一样,可是仅仅能左右浮动。

正是由于浮动的这样的特性,导致本属于普通流中的元素浮动之后,包括框内部由于不存在其它普通流元素了,也就表现出高度为0(高度塌陷)。在实际布局中,往往这并非我们所希望的,所以须要闭合浮动元素,使其包括框表现出正常的高度。

二、清除浮动的原理——了解 hasLayout 和 Block formatting contexts
先看一下清理浮动的各种方法:

1)加入额外标签
这是在学校老师就告诉我们的 一种方法,通过在浮动元素末尾加入一个空的标签比如 <div style=”clear:both”></div>,其它标签br等亦可。
  1. <div class="warp" id="float1">
  2. <h2>1)加入额外标签</h2>
  3. <div class="main left">.main{float:left;}</div>
  4. <div class="side left">.side{float:right;}</div>
  5. <div style="clear:both;"></div>
  6. </div>
  7. <div class="footer">.footer</div>
长处:通俗易懂,easy掌握

缺点:能够想象通过此方法,会加入多少无意义的空标签,有违结构与表现的分离,在后期维护中将是噩梦,这是坚决不能忍受的,所以你看了这篇文章之后还是建议不要用了吧。


2)使用 br标签和其自身的 html属性
这种方法有些小众,br 有 clear=“all | left | right | none” 属性

  1. <div class="warp" id="float2">
  2. <h2>2)使用 br标签和其自身的 html属性</h2>
  3. <div class="main left">.main{float:left;}</div>
  4. <div class="side left">.side{float:right;}</div>
  5. <br clear="all" />
  6. </div>
  7. <div class="footer">.footer</div>
长处:比空标签方式语义稍强,代码量较少
缺点:相同有违结构与表现的分离,不推荐使用


3)父元素设置 overflow:hidden
通过设置父元素overflow值设置为hidden;在IE6中还须要触发 hasLayout ,比如 zoom:1;

  1. <div class="warp" id="float3" style="overflow:hidden; *zoom:1;">
  2. <h2>3)父元素设置 overflow </h2>
  3. <div class="main left">.main{float:left;}</div>
  4. <div class="side left">.side{float:right;}</div>
  5. </div>
  6. <div class="footer">.footer</div>
长处:不存在结构和语义化问题,代码量极少
缺点:内容增多时候easy造成不会自己主动换行导致内容被隐藏掉,无法显示须要溢出的元素;04年POPO就发现overflow:hidden会导致中键失效,这是我作为一个多标签浏览控所不能接受的。所以还是不要使用了


4)父元素设置 overflow:auto 属性
相同IE6须要触发hasLayout,演示和3差点儿相同
长处:不存在结构和语义化问题,代码量极少
缺点:多个嵌套后,firefox某些情况会造成内容全选;IE中 mouseover 造成宽度改变时会出现最外层模块有滚动栏等,firefox早期版本号会无故产生focus等, 请看 嗷嗷的 Demo ,不要使用

5)父元素也设置浮动
长处:不存在结构和语义化问题,代码量极少
缺点:使得与父元素相邻的元素的布局会受到影响,不可能一直浮动到body,不推荐使用

6)父元素设置display:table
长处:结构语义化全然正确,代码量极少
缺点:盒模型属性已经改变,由此造成的一系列问题,得不偿失,不推荐使用

7)使用:after 伪元素
须要注意的是 :after是伪元素(Pseudo-Element),不是伪类(某些CSS手冊里面称之为“伪对象”),非常多清除浮动大全之类的文章都称之为伪类,只是csser要严谨一点,这是一种态度。
因为IE6-7不支持:after,使用 zoom:1触发 hasLayout。


原文所有代码例如以下:

  1. <style type="text/css"> .clearfix:after { content: ".";display: block;height: 0;clear: both;visibility: hidden; }  .clearfix {display: inline-block;}  /* for IE/Mac */  </style><!--[if IE]> <style type="text/css">.clearfix {zoom: 1;/* triggers hasLayout */ display: block;/* resets display for IE/Win */}</style> <![endif]-->
鉴于 IE/Mac的市场占有率极低,我们直接忽略掉,最后精简的代码例如以下:
  1. .clearfix:after {content:"."; display:block; height:0; visibility:hidden; clear:both; }
  2. .clearfix { *zoom:1; }
长处:结构和语义化全然正确,代码量居中
缺点:复用方式不当会造成代码量添加


小结
通过对照,我们不难发现,事实上以上列举的方法,无非有两类:
其一,通过在浮动元素的末尾加入一个空元素,设置 clear:both属性,after伪元素事实上也是通过 content 在元素的后面生成了内容为一个点的块级元素;
其二,通过设置父元素 overflow 或者display:table 属性来闭合浮动,我们来探讨一下这里面的原理。


在CSS2.1里面有一个非常重要的概念,可是国内的技术博客介绍到的比較少,那就是Block formatting contexts(块级格式化上下文),下面简称 BFC。
CSS3里面对这个规范做了修改,称之为:flow root,而且对触发条件进行了进一步说明。



那么怎样触发BFC呢?

  • float 除了none以外的值
  • overflow 除了visible 以外的值(hidden,auto,scroll )
  • display (table-cell,table-caption,inline-block)
  • position(absolute,fixed)
  • fieldset元素

须要注意的是,display:table 本身并不会创建BFC,可是它会产生匿名框(anonymous boxes),而匿名框中的display:table-cell能够创建新的BFC,换句话说,触发块级格式化上下文的是匿名框,而不是display:table。所以通过display:table和display:table-cell创建的BFC效果是不一样的。


fieldset 元素在www.w3.org里眼下没有不论什么有关这个触发行为的信息,直到HTML5标准里才出现。有些浏览器bugs(Webkit,Mozilla)提到过这个触发行为,可是没有不论什么官方声明。实际上,即使fieldset在大多数的浏览器上都能创建新的块级格式化上下文,开发人员也不应该把这当做是理所当然的。CSS 2.1未定义哪种属性适用于表单控件,也未定义怎样使用CSS来给它们加入样式。用户代理可能会给这些属性应用CSS属性,建议开发人员们把这样的支持当做实验性质的,更高版本号的CSS可能会进一步规范这个。

BFC的特性:
1)块级格式化上下文会阻止外边距叠加当两个相邻的块框在同一个块级格式化上下文中时,它们之间垂直方向的外边距会发生叠加。换句话说,假设这两个相邻的块框不属于同一个块级格式化上下文,那么它们的外边距就不会叠加。

2)块级格式化上下文不会重叠浮动元素依据规定,一个块级格式化上下文的边框不能和它里面的元素的外边距重叠。这就意味着浏览器将会给块级格式化上下文创建隐式的外边距来阻止它和浮动元素的外边距叠加。因为这个原因,当给一个挨着浮动的块级格式化上下文加入负的外边距时将会不起作用(Webkit和IE6在这点上有一个问题——能够看这个測试用例)。 

3)块级格式化上下文通常能够包括浮动详见: W3C CSS2.1 - 10.6.7 'Auto' heights for block formatting context roots 
  
通俗地来说:创建了 BFC的元素就是一个独立的盒子,里面的子元素不会在布局上影响外面的元素,反之亦然,同一时候BFC任然属于文档中的普通流。

至此,您也许明确了为什么 overflow:hidden或者auto能够闭合浮动了,真是由于父元素创建了新的BFC。对于张鑫旭在对《overflow与zoom”清除浮动”的一些认识 》一文中对于用包裹来解释闭合浮动的原理,我认为是不够严谨的,并且没有根据。并且说道“Firefox等浏览器并没有haslayout的概念”,那么现代浏览器是有BFC的,从表现上来说,hasLayout 能够等同于 BFC。

IE6-7的显示引擎使用的是一个称为布局(layout)的内部概念,因为这个显示引擎自身存在非常多的缺陷,直接导致了IE6-7的非常多显示bug。当我们说一个元素“得到 layout”,或者说一个元素“拥有 layout” 的时候,我们的意思是指它的微软专有属性 hasLayouthttp://msdn.microsoft.com/worksh ... rties/haslayout.asp 为此被设为了 true 。IE6-7使用布局的概念来控制元素的尺寸和定位,那些拥有布局(have layout)的元素负责本身及其子元素的尺寸设置和定位。假设一个元素的 hasLayout 为false,那么它的尺寸和位置由近期拥有布局的祖先元素控制。

触发hasLayout的条件:

在 IE7 中,overflow 也变成了一个 layout 触发器:
  • overflow: hidden|scroll|auto ( 这个属性在IE之前版本号中没有触发 layout 的功能。 )
  • overflow-x|-y: hidden|scroll|auto (CSS3 盒模型中的属性,尚未得到浏览器的广泛支持。他们在之前IE版本号中相同没有触发 layout 的功能)

hasLayout更具体的解释请參见 old9翻译的 大名鼎鼎的 《On having layout》一文(英文原文:http://www.satzansatz.de/cssd/onhavinglayout.htm),因为old9博客被墙,中文版地址:
IE8使用了全新的显示引擎,据称不使用 hasLayout属性了,因此攻克了非常多深恶痛绝的bug,但 IE8~IE11 通过「document.documentElement.currentStyle.hasLayout」依旧能够获得 hasLayout 的标志,我写了一个測试 Demo(IE8 中 zoom:1 返回 false),更具体的请看《IE8 haslayout = true》
T1ObpkFfXeXXcHbb.L-648-388.png

综上所述:在支持BFC的浏览器(IE8+,firefox,chrome,safari)通过创建新的BFC闭合浮动;在不支持 BFC的浏览器 (IE6-7),通过触发 hasLayout 闭合浮动。


三、闭合浮动方法——精益求精
上面已经列举了7种闭合浮动的方法,通过第三节分析的原理,我们发现事实上很多其它的:display:table-cell,display:inline-block等仅仅要触发了BFC的属性值都能够闭合浮动。从各个方面比較,after伪元素闭合浮动无疑是相对照较好的解决方式了,以下具体说说该方法。
  1. .clearfix:after {content:"."; display:block; height:0; visibility:hidden; clear:both; }
  2. .clearfix { *zoom:1; }
1) display:block 使生成的元素以块级元素显示,占满剩余空间;
2) height:0 避免生成内容破坏原有布局的高度。
3) visibility:hidden 使生成的内容不可见,并同意可能被生成内容盖住的内容能够进行点击和交互;
4)通过 content:"."生成内容作为最后一个元素,至于content里面是点还是其它都是能够的,比如oocss里面就有经典的 content:"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",有些版本号可能content 里面内容为空,一丝冰凉是不推荐这样做的,firefox直到7.0 content:”" 仍然会产生额外的空隙;
5)zoom:1 触发IE hasLayout。
通过分析发现,除了clear:both用来清除浮动的,其它代码无非都是为了隐藏掉content生成的内容,这也就是其它版本号的闭合浮动为什么会有font-size:0,line-height:0。


精益求精方案一:
相对于空标签闭合浮动的方法代码似乎还是有些冗余,通过查询发现Unicode字符里有一个“零宽度空格”,也就是U+200B,这个字符本身是不可见的,所以我们全然能够省略掉 visibility:hidden了
  1. .clearfix:after {content:"\200B"; display:block; height:0; clear:both; }
  2. .clearfix { *zoom:1; }

精益求精方案二:

由Nicolas Gallagher 大湿提出来的,原文:A new micro clearfix hack,该方法也不存在firefox中空隙的问题。
  1. /* For modern browsers */
  2. .cf:before,.cf:after {
  3. content:"";
  4. display:table;
  5. }
  6. .cf:after { clear:both; }/* For IE 6/7 (trigger hasLayout) */
  7. .cf { zoom:1; }

须要注意的是:

上面的方法用到了  :before伪元素,非常多人对这个有些迷惑,究竟我什么时候须要用before呢?为什么方案一没有呢?事实上它是用来处理margin边距重叠的,因为内部元素 float 创建了BFC,导致内部元素的margin-top和 上一个盒子的margin-bottom 发生叠加。假设这不是你所希望的,那么就能够加上before,假设仅仅是单纯的闭合浮动,after就够了!并非如同大漠《Clear Float》一文所说的:但仅仅使用clearfix:after时在跨浏览器兼容问题会存在一个垂直边距叠加的bug,这不是bug,是BFC应该有的特性。
7da7b2c795a2e4249ca89a4900f3ec2b.jpg

在实际开发中,改进方案一因为存在Unicode字符不适合内嵌CSS的GB2312编码的页面,使用方案7全然能够解决我们的需求了,改进方案二等待大家的进一步实践。方案3、4通过overflow闭合浮动,实际上已经创建了新的 块级格式化上下文,这将导致其布局和相对于浮动的行为等发生一系列的变化,清除浮动仅仅只是是一系列变化中的一个作用而已。所以为了闭合浮动去改变全局特性,这是不明智的,带来的风险就是一系列的bug,比方firefox 早期版本号产生 focus,截断绝对定位的层等等。始终要明确,假设单单仅仅是须要闭合浮动,overflow就不要使用,而不是某些文章所说的“慎用”。

posted @ 2014-06-30 20:03  mengfanrong  阅读(200)  评论(0编辑  收藏  举报