CSS浮动float详解

作者: 张歆琳 | 来源:发表于2016-04-26 17:28 被阅读13509次

    CSS里浮动float是个概念比较暧昧的属性,撸主最早对浮动float的认识是基于布局的,认为float元素就是用于:“让block元素无视float元素,让inline元素让流水一样围绕着float元素”来实现浮动布局。现在想想,当初真是图样图森破。

    其实这个属性撸主一直是比较模糊的,感觉似懂非懂。本着和自己死磕的精神,在参考了许多大神的博文后,将我的理解整理归纳在本文中,希望能可以帮助到你。当然撸主水平有限,如有错误敬请指出。

    以下是网上大神关于float的优秀文章:

    CSS浮动(float,clear)通俗讲解

    CSS float浮动的深入研究、详解及拓展(一)

    CSS float浮动的深入研究、详解及拓展(二)

    那些年我们一起清除过的浮动

    浮动float的本意:

    传统如C++,Java等编程语言一个API可能只能对应做一件事,即使有了模板和泛型编程,通常也只能做某一类事。但CSS是门相当灵活的语言。某个CSS属性被用于的场景,可能会完全违背当初创造该CSS属性的本意。在CSS的世界里,想实现某个效果,会有很多方法。那究竟选择哪种方法呢?有很多判断标准,如重绘,如回流,如极简主义。撸主也有一个不成熟的判断标准就是:根据该CSS属性被创造时的本意,该用哪个属性就用哪个属性。
    那浮动float的本意是什么呢?是:让文字像流水一样环绕浮动元素。

    怎样才能实现该效果呢?用 包裹性高度欺骗

    特性一:包裹性

    例1:首先来看浮动float的包裹性,所谓一图胜千言:

    相关代码非常简单:

    <div style="border:4px solid blue;">
        <img src="img/25/1.jpg" />
    </div>
    <div style="border:4px solid red;float:left;">
        <img src="img/25/2.jpg" />
    </div>
    

    所谓包裹性一目了然。block元素不指定width的话,默认是100%,一旦让该div浮动起来,立刻会像inline元素一样产生包裹性,宽度会跟随内容自适应。(这也是通常float元素需要手动指定width的原因)

    再加上一个div的话,效果如下:

    <div style="border:4px solid blue;">
        <img src="img/25/1.jpg" />
    </div>
    <div style="border:4px solid red;float:left;">
        <img src="img/25/2.jpg" />
    </div>
    <div style="border:4px solid green;">
        <img src="img/25/3.jpg" /> 
    </div>
    

    效果非常近似于display:inline-block。但相比之下,浮动能设定为左浮和右浮,但display:inline-block都是从左到右排列的。(还有些细微差别,两个display:inline-block间会有空隙,但两个float间没有。这不是本篇的主题,暂时略过)

    特性二:高度欺骗

    (首先声明:其实是CSS层级在起作用,但CSS层级适合单独写一篇,内容实在太多,不适合在这里展开,就理解为高度欺骗吧)

    例1中浮动float被设在了外围div上,因此高度欺骗性没体现出来。现在给内层img元素设定float。所谓一图胜千言:


    <div style="border:4px solid blue;">
        <img src="img/25/1.jpg" />
    </div>
    <div style="border:4px solid red;">
        <img style="border:4px solid yellow;float:left;" src="img/25/2.jpg" />
    </div>
    

    和例子1唯一的区别就是:将外层div的float移到内层img中。这下高度欺骗性体现出来了。例1中给外层div加上浮动,因此外层div会有包裹性,其内容是img图片,所以可以看到红色边框包裹着img。

    例2中外层div没有了浮动,因此红色边框宽度默认是100%全屏。其内容img由于加上了float,使得该img具有了欺骗性。float给img施了个障眼法,让该img的inline-height高度塌陷为0了。这样外层div计算高度时,认为img的高度为0,相当于div的content的高度为0,因此红色边框看起来是一条直线。

    但请注意障眼法毕竟是障眼法,并不是真的让img的高度塌陷为0了,可以看到上图中img的黄色边框还是有正常高度的。如果给div里加点文字,效果如下:

    可以看到,外层div在没有手动设定height的前提下,其高度是由内部content的最大高度决定的,由于img的float使得img具有高度塌陷的欺骗性,让div误以为img的line-height为0,因此div的高度就是文字的匿名inline-box的inline-height。

    因此浮动并不是让元素的高度塌陷了,而是让元素具有高度塌陷的欺骗性。骗谁?骗别人!但骗不了自己,元素自身还是有高度的(见上图的黄框)。

    回过头再看看浮动float的本意:让文字像流水一样环绕图片。重要的事情多看几遍...给div设定一个width:200px,并加点文字吧:

    这就是浮动元素的本意。该效果是很难被其他CSS属性等价地模拟的。

    但就像开头说的,CSS强大的灵活性使得很多CSS属性被用于了创造者都没想到的场景。以float为例,就被广泛用于了布局。是好是坏呢?不知道!西红柿臭鸡蛋先别急着扔。既然撸主不知道,还废话什么?先看看float布局的问题。渣浪微博改版前的好友列表用浮动布局,效果如下:

    <ul>
        <li style="width:138px;margin:0 10px;text-align: center;float:left;">
            <div><img src="img/25/1.jpg" />尼古拉斯.旺财</div>
        </li>
        <li style="width:138px;margin:0 10px;text-align: center;float:left;">
            <div><img src="img/25/2.jpg" />功夫熊猫</div>
        </li>
        <li style="width:138px;margin:0 10px;text-align: center;float:left;">
            <div><img src="img/25/3.jpg" />月野兔</div>
        </li>
        <li style="width:138px;margin:0 10px;text-align: center;float:left;">
            <div><img src="img/25/4.jpg" />猫女郎</div>
        </li>
    </ul>
    

    每个li都设为浮动和定宽,实现了水平布局。但如果好友再长点呢?效果如下:

    错行啦!常见的修正方案是手动设定一个高度,让文字固定显示一行,用裁掉超行文字的代价以避免错行问题。在撸主看来这就是让CSS属性用于不合原意处的局限性。设固定高度是OK,但如果哪天设计师觉得姓名需要显示两行呢,那固定高度就需要重新计算重新变。如果设计师觉得需要拓宽俄罗斯市场,姓名要显示三行呢?再把固定高度改大点。如果未来Boss脑袋一拍,咦,能不能高度自适应呢?由姓名最大高度的好友来决定每行的高度。你是不是会有准备一下简历的冲动?

    当然现实没这么夸张,高度自适应是个烂大街的技术,将浮动float改成 display:inline-block 就行了,效果如下:

    代码只需将上面float:left;替换成display: inline-block;,没对齐只需给li加上个vertical-align: top;(上面提到过,相比float,display:inline-block中间会有空隙,眼神好的可以从图中就能看出来,解决方案不是本篇的主题,可以问度娘)。这下高度自适应了,每行的高度都是以名字最长的高度为准。

    回过头看用float来水平布局。是好是坏呢?好处是上手简单,随便什么程度的CSSer都能搞定。坏处是有时需要定高难以自适应。而display:inline-block;属性可是根正苗红的水平布局的属性,可以用其替代float。让float尽量多的干其本职工作:让文字像流水一般环绕浮动元素。所以撸主不知道答案。或许也根本没有正确答案,不停的推翻原有的认识和想法人才能进步。

    PS:用浮动布局还有个坏处就是IE6下可能会有问题。但在IE6横行时期,撸主经验尚浅,现在项目里早已明确弃用IE6,撸主也懒得挖坟验证了。

    清除浮动:

    这个相对比较简单了。用clear即可。稍微要注意的是,clear是仅作用于当前元素,例如元素A是浮动元素,靠左显示。元素B是block元素紧跟在A后面。此时要清除浮动,是在B上设clear:left。你在A上设clear:right是没有用的,因为A的右边没有浮动元素。

    但真这么简单吗?图样图森破。

    先明确一个概念,用clear确实能达到我们期望的清除浮动的效果,这点没异议。但深入点看,究竟是清除了什么样的浮动呢?一图胜千言:

    代码:(给页脚加上clear:left)

    <div style="border:4px solid blue;">
        <div style="width:200px;border:4px solid red;float:left;">
            我是浮动元素1
        </div>
        <div style="width:200px;border:4px solid yellow;float:left;">
            我是浮动元素2
        </div>
    </div>
    <div style="border:4px solid gray;clear:left;">我是页脚</div>
    

    因为浮动元素的高度欺骗性,导致外层div失去了高度(蓝色边框成了一条线)。为了让页脚显示到浮动元素下面,对页脚应用了clear:left。这是常规做法,没有任何新奇之处。但是外层div的高度仍旧处于塌陷状态,我们脑海真真正期望的清除浮动后的样子难道不是下面这样吗?

    让清除浮动后,原本被欺骗的外层div获得正确的高度!借用文首大神链接的说法,我们脑中期望的其实并不是上图的清除浮动,而是下图的 闭合浮动

    闭合浮动

    闭合浮动的实现方法很多,常见的是最后增加一个清除浮动的子元素:

    <div style="border:4px solid blue;">
        <div style="width:200px;border:4px solid red;float:left;">
            我是浮动元素1
        </div>
        <div style="width:200px;border:4px solid yellow;float:left;">
            我是浮动元素2
        </div>
        <div style="clear:both;"></div>  //加上空白div节点来闭合浮动
    </div>
    <div style="border:4px solid gray;">我是页脚</div>
    

    缺点是会增加一个DOM节点。(话说当初撸主不知道在哪里看到这个做法时,作者并未讲这么做的原因,导致撸主不明白明明页脚加一个clear属性就能搞定的事,为何要大动干戈加一个DOM节点)

    方法二:同样可以在最后增加一个清除浮动的br:将上面代码中<div style=”clear:both;”></div>替换成<br clear=”all” />即可。语义上比空的div标签稍微好一点,但同样会增加一个DOM节点。

    方法三:父元素设置 overflow:hidden(如果你还要兼顾IE6的话,加上*zoom:1;来触发hasLayout)

    <div style="border:4px solid blue;overflow:hidden;">
        <div style="width:200px;border:4px solid red;float:left;">
            我是浮动元素1
        </div>
        <div style="width:200px;border:4px solid yellow;float:left;">
            我是浮动元素2
        </div>
    </div>
    <div style="border:4px solid gray;">我是页脚</div>
    

    这看起来很奇怪。因为子元素的浮动的高度欺骗,导致父元素误认为content高度为0(即蓝色边框为一条线),所以父元素设成overflow:hidden溢出隐藏的话,直觉上应该子元素由于溢出导致不显示才对,即整个页面只显示页脚。但实际效果,父元素设成overflow:hidden溢出隐藏后,竟然神奇地出现了闭合浮动的效果(蓝色边框正确获得了高度)。这是怎么回事呢?靠的是BFC,但BFC说起来又是很长一篇,先略过。你可以先简单地这么理解:浏览器厂商认为要让超出边框部分可以被修剪掉,那么前提就是父元素要正确获得高度,即父元素不能被欺骗导致高度塌陷。浏览器正确获得子元素的高度后给父元素重新设置高度。虽然权威解释肯定是BFC,但撸主这样理解了很久...

    方法四:同上面将父元素设置 的overflow:hidden改成auto,不赘述

    方法五:父元素也设成float。这样确实实现了闭合浮动,但页脚将上移,所以页脚仍旧需要clear:left。还有个缺点是由于浮动的包裹性,你确定父元素真的设成float对页面布局不会产生影响吗?

    方法六:父元素设置display:table。效果OK,页脚也不需要设clear:left,但父元素的盒子模型被改变了,请先确认下这样的改动对页面布局不会产生影响吗?

    方法七:用:after伪元素,思路是用:after元素在div后面插入一个隐藏文本”.”,隐藏文本用clear来实现闭合浮动:

    .clearfix:after {
        clear: both;
        content: ".";   //你头可以改成其他任意文本如“abc”
        display: block;
        height: 0;      //高度为0且hidden让该文本彻底隐藏
        visibility: hidden;
    }
    .clearfix {
        *zoom: 1;
    }
    
    <div style="border:4px solid blue;" class="clearfix">
        <div style="width:200px; border:4px solid red; float:left;">
            我是浮动元素1
        </div>
        <div style="width:200px; border:4px solid yellow; float:left;">
            我是浮动元素2
        </div>
    </div>
    <div style="border:4px solid gray;">我是页脚</div>
    

    这个方法很不错,就是相比上面的方法,理解起来稍微有一点点难度。但也仅增加一点点而已。

    总结:

    第一篇文章啰嗦几句。其实本文开头链接的几篇博文写的比我好,为何我要写这篇博文?是我自信能比大神们写的更好吗?当然不是。之所以写技术博文是因为这是一个非常好的学习总结的方式。就在写之前我仍旧以为已经非常了解float了,但真要提笔写,又发现自己在非常多的细节方面还是一知半解。只有真正静下心来,边思考边做demo验证边总结,才能将自己的知识总结成一篇博文。

    技术博文不是散文,必须条理清晰,结构严谨,经得起推敲,不能满嘴跑火车,否则就误人子弟了。以前看优秀的文章时,偶尔会看到作者吐槽:这段话足足写了一下午。这篇文章写了三天三夜。看着文章字数又不多,最多10分钟就看完了,但如果你没有真正提笔写过,你可能体会不到期间的困难。

    作为一个起步非常晚的前端工程师(I know…I late to the Party.)我只能死磕自己。

    相关文章

      网友评论

      • zvmal:楼主写得很棒,解决了心中的很多困惑,感谢楼主。:+1:
      • 随机重组:写东西很容易,但写的好真不容易,试着用自已的话去理解总结所学知识,真是一种好方法,对于初学者,你看懂不见得能用会用,眼高手低了。
      • 晚安_喵:真的很棒呀,楼主加油哟💪
      • Lonely_Devil:通俗易懂
      • 8d1e3ab7e317:写得很好,描述得很清楚,算是知根知底了:smile:
      • 絮汀:我专门申请了一个号来赞你
        写的太好了
        必须赞啊
        张歆琳:@絮汀 太客气了:smile:
      • DHFE:最后总结很赞,真的开始写博客必须脑子非常清晰,不然写出的东西没有任何价值。
      • 淡蓝的云:前部分非常精彩,尤其是介绍浮动的时候。
        解决方案说的仓促。
      • 抓手时光之刃:float修改成inline-block有潜在的BUG
        换行默认存在匿名inline元素,导致宽度上有误差。比如两个inline-block都是50%,会导致换行的。可以通过设置font-size来解决,但是语义上非常不好
      • 鸭梨山大哎:Float 不应该用来布局:smile:
      • 董懂同学:我最近看了好多浮动的文章,但可能我水平太菜,没怎么看懂,你这篇我算是看懂了。:+1:
      • 7b2c11d5631c:嗯嗯。。。
      • bc805e9c6a89:写的真好呀!作为一个刚进入前端的大学狗,看了其他很多讲float的文章,感觉都没有楼主讲的通俗易懂
      • 357819b79929:作为一个起步非常晚的前端工程师,我只能死磕自己
      • c1c4726cc9fc:比那个引用的几篇看的好懂点。。。。。。
      • c1c4726cc9fc:不错不错啦啦,你那个,引用的几篇看的好懂点。。。。。。
      • 1acb14a5a489:谢谢楼主,受益匪浅,ps,我也是个起步非常晚的前端菜鸟
      • hcxowe:除开知识点,撸主说的也很有道理

      本文标题:CSS浮动float详解

      本文链接:https://www.haomeiwen.com/subject/gkoqrttx.html