CSS 浮动

说来让自己羞耻:自称写了 HTML + CSS 快一年的我连最基础的浮动都没搞清楚过。脑子里关于浮动的知识点一直处于“大概、好像、应该”这种混沌状态。这样的恶果就是:平时虽然我也在像模像样的在用 float 布局,却常常因为摸不清浮动了的元素的怪脾气而感到沮丧,这种无法掌控页面元素的感觉太糟糕了,就像面对淘气顽皮切不听话的孩子却又手足无措。治本才是关键,我尝试着去重新审视浮动,试图去深入理解浮动,于是便生出此文——我以自己的方式去解读浮动。

1. 先撇开浮动不谈

在说浮动之前,我想先谈谈浮动之外的东西:正常的普通流是什么样子。正常的普通流中,如果不给块状元素(Block Element)设置宽度的话,那么块状元素便会默认霸占父容器的一整行,即使为其设置了宽度,看似块状元素规矩了,但是还是霸占着父容器的一整行。粗俗的说法就是:“占着茅坑不拉粑粑”,块状元素是很强势的,他们总是一行一行的排列。如果父容器的宽高小于其所包含的块状元素,那么块状元素就很不客气的溢出了。

行内元素(Inline Block Element)则要温柔、讲道理得多,它只要占据自己那一部分“宽度”,具体大小是:width+padding-left+padding-right+margin-right+margin-left(我们假设为其设置了paddingmargin)。如果父容器的宽度不足以存放其包含的行内元素,行内元素也会通情达理的将溢出了的部分另起一行,依此类推,直到完全铺陈完毕。如果父容器的高度无法容纳其包含的行内元素的子容器,那么行内元素也只能无奈的溢出了。顺便说一句,对行内元素设置heightwidth以及垂直方向的margin是无效滴。

文字总是过于枯燥乏味,我们来看实际效果。比如这里是一个简单的HTML页面只有一个简单的div块与span元素。

<!doctype html>
<html lang="en">
    <head>
       <meta charset="UTF-8">
       <title>Document</title>
    </head>
<body>
    <div class="div1">
        这是一个未浮动的DIV
    </div>
    <span>
        这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。
    </span>
</body>
</html>

这里是CSS代码:

body {
    paddinng: 0;
    margin: 0;
}
.div1 {
    width: 500px;
    height: 100px;
    background-color: #0087E6; /**这里标注颜色只是为了便于区分块状元素 **/
}

以下是实际效果:

这是一个未浮动的DIV

这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。

By the way,块状元素与行内元素是可以互换的,那就是用:

display: block;     /** 设置为块状元素 **/
display: inline-block;  /** 设置为行内元素 **/

2. 如何浮动?

“为什么要浮动”?用我的戏说就是:

让块状元素交出自己抢占的那一部分,实现页面更加灵活的布局,而不是在块状元素元素霸占下的单调乏味的单行布局。

让块状元素乖乖交出自己霸占的那一部分的魔法语句也很简单:

float: left;    /** 向左浮动 **/
float: right;   /** 向右浮动 **/

还是那个简单的HTML页面。 如下我们设置一个块状元素左浮动:

.div1 {
    width: 500px;
    height: 100px;
    float: left;
    background-color: #ccc;
}

这是浮动后的页面效果:你看,这样span元素就可以使用浮动后div元素空出的位置了。

这是一个左浮动的DIV

这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。

3. 什么是浮动?

3.1 浮动后的元素有哪些特性:

  1. 浮动后的元素会按照浮动的方向向左或者向右移动,直到遇到:
    1. 父容器边缘;
    2. 或者另外一个浮动元素的边缘(整个盒子模型的边缘即margin边框);
    3. 再或者是位于浮动元素前的标准普通流中快元素元素的边缘(整个盒子模型的边缘)。
  2. 浮动元素会脱离标准普通流,所以标准普通流中的块状元素的表现就像不存在浮动元素一样。
  3. 浮动元素会脱离标准普通流,标准普通流中的行内元素还会受到浮动的块状元素的影响。
  4. 浮动对所有标准个普通中的元素都有效。
  5. 如果该行剩余空间不足以容纳浮动元素,那么浮动元素会另起一行。

看完上述定义,如果你觉得晕头转向(我觉得那是应该的),那么请看下面我的示例;

3.2 浮动到父容器边缘:

浮动第一定量(由本人胡编乱造):被浮动的元素会奋不顾身的沿着浮动方向移动到父容器的边缘以及顶部(如果不算其他因素的影响的话),示例见上一个div左浮动文字环绕效果,这里不再赘述。

3.2 浮动到另一个浮动元素的边缘:

继续是一张简单的HTML页面,有三个div块:

<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
        <div class="div1">这是一个左浮动的DIV</div>
        <div class="div2">这是一个左浮动的DIV</div>
        <div class="div3">这是一个左浮动的DIV</div>
    </body>
</html>

CSS代码,这里我们设三个div块都左浮动:

body {
    padding: 0;
    margin: 0;
}
.div1 {
    width: 200px;
    height: 100px;
    float: left;
    background-color: #0087E6; /**这里标注颜色只是为了便于区分块状元素**/
}
.div2 {
    width: 200px;
    height: 100px;
    padding: 10px; /**设置内边距**/
    margin: 10px; /**设置外边距**/
    float: left;
    background-color: #178448; /**这里标注颜色只是为了便于区分块状元素**/
}
.div3 {
    width: 200px;
    height: 100px;
    float: left;
    background-color: #c4270d;  /**这里标注颜色只是为了便于区分块状元素**/
}
这是一个左浮动的DIV1
这是一个左浮动的DIV2
这是一个左浮动的DIV3

这里我想强调的盒子模型的边缘,正如示例所展示的(这里假设大家已经完全理解了盒子模型)。如果很不幸,某行剩余空间已经容纳不下该浮动元素,那么该浮动元元素只能另起一行移动到父容器内框边缘,垂直方向只能移动到最高盒模型的边缘。我们看示例:

这是一个30%宽度左浮动的DIV1
这是一个30%宽度左浮动的DIV2
这是一个30%宽度左浮动的DIV3
这是一个40%宽度左浮动的DIV4

3.3 位于标准普通流的元素的边缘

正如下面会提到的:位于标准普通流的元素回无视浮动了的元素,但是浮动元素却不得正视这些位于标准普通流的元素们。唉,这些可怜的被流放的人们。话不多说,再次上例子;这里我们的HTML稍有改变(好吧,我再也不想喋喋不休的去强调这些无关痛痒的小事了)。

<!doctype html>
<html lang="en">
    <head>
           <meta charset="UTF-8">
           <title>Document</title>
    </head>
    <body>
        <span class="span1">这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。</span>
        <div class="div1">这是一个未浮动的DIV</div>
        <div class="div2">这是一个左浮动的DIV</div>
    </body>
</html>

CSS代码,这里我们设第三个个div块都左浮动:

body {
    padding: 0;
    margin: 0;
}
.span1 {
    padding: 10px; /** 设置内边距 **/
    margin: 10px; /** 设置外边距 **/
}
.div1 {
    width: 200px;
    height: 100px;
    margin-bottom: 10px;
    background-color: #0087E6;  /**  这里标注颜色只是为了便于区分块状元素 **/
}
.div2 {
    width: 200px;
    height: 100px;
    float: left;
    background-color: #c4270d;  /**  这里标注颜色只是为了便于区分块状元素 **/
}
这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。

这是一个未浮动的DIV1
这是一个左浮动的DIV2

示例很好证明了结论:浮动元素由于之前处于标准普通流的spandiv元素的存在,垂直方向上而只能在其之后了。不过这样的情况仅仅适用于处于标准普通流的块状元素,行内元素的表现有点怪异。

<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
        <span class="span1">这是一段行内元素,他们现在处于标准普通流中。但是他们的表现并不象处于标准普通流的块状元素那样,浮动的块状元素会乖乖的处于标准普通流中的块状元素的边缘(我指的是margin边框),是啊我说话真的很啰嗦,想必你也看到了。这是一段行内元素,他们现在处于标准普通流中。但是他们的表现并不象处于标准普通流的块状元素那样,浮动的块状元素会乖乖的处于标准普通流中的块状元素的边缘(我指的是margin边框),是啊我说话真的很啰嗦,想必你也看到了。</span>
        <div class="div1">这是一个左浮动的DIV</div>
    </body>
</html>

CSS代码,这里我们设第二个div块左浮动:

body {
    padding: 0;
    margin: 0;
}
.span1 {
    padding: 10px;          /** 设置内边距 **/
    margin: 10px;           /** 设置外边距 **/
}
.div1 {
    width: 200px;
    height: 200px;
    float: left;
    margin-bottom: 10px;
    background-color: #0087E6;  /**  这里标注颜色只是为了便于区分块状元素 **/
}
这是一段行内元素,他们现在处于标准普通流中。但是他们的表现并不象处于标准普通流的块状元素那样,浮动的块状元素会乖乖的处于标准普通流中的块状元素的边缘(我指的是margin边框),是啊我说话真的很啰嗦,想必你也看到了。这是一段行内元素,他们现在处于标准普通流中。但是他们的表现并不象处于标准普通流的块状元素那样,浮动的块状元素会乖乖的处于标准普通流中的块状元素的边缘(我指的是margin边框),是啊我说话真的很啰嗦,想必你也看到了。

这是一个左浮动的DIV1

这里我得声明下:这段Demo可能并不能很好展示,行内元素的怪异表现。事实上随着span元素内字数的改变,行内元素可能会表现得像块状元素一样:浮动了的块状元素会乖乖留在行内元素的边缘。但有时候,浮动的块状元素会侵入行内元素一部分,而行内元素会包围住浮动了的块状元素。

3.4 被忽略了的浮动元素

好吧,上文也提到了,如果处于标准普通流中的块状元素之前有元素浮动(无论是浮动了的块状元素还是浮动了的行内元素)那么,处于标准普通流中的块状元素会表现的如同这些浮动元素不存在一样,也就是说这些浮动元素被无视了!

<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
        <span class="span1">这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。</span>
        <div class="div1">这是一个左浮动的DIV</div>
    </body>
</html>

CSS代码,为了说明情况的特殊性:这里我们设第一个span块左浮动:

body {
    padding: 0;
    margin: 0;
}
.span1 {
    padding: 10px;      /** 设置内边距 **/
    margin: 10px;           /** 设置外边距 **/
    float: left;
}
.div1 {
    width: 200px;
    height: 200px;
    margin-bottom: 10px;
    background-color: #0087E6;  /**  这里标注颜色只是为了便于区分块状元素 **/
}
这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。

这是一个左浮动的DIV1

如Demo所示,span元素与div元素发生了重叠,原因如上文所述,div元素表现得如同浮动了的span元素一样。如果浮动的是块状元素,情况也一样。这里我就不贴代码了,直接看示例。

这是一个左浮动的200px高的DIV1

这是一个未浮动的400px高的DIV2

3.5 特殊的行内元素

上文各种示例表明,标准普通流中的行内元素会受到浮动元素的影响,这个不再赘述。但是细心的你也许会发现,上文所有示例中的所有浮动元素均为块状元素,我也说过所有元素均可浮动,那么言下之意行内元素也可以浮动,不过浮动了后的行内元素表现同样有趣。我们看示例:

<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
        <span class="span1">这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。</span>
        <div class="div1">这是一个未浮动的DIV</div>
    </body>
</html>

CSS代码,为了说明情况的特殊性:这里我们设第一个span块左浮动,并且为其设置宽度(我不是在开玩笑):

body {
    padding: 0;
    margin: 0;
}
.span1 {
    padding: 10px;          /** 设置内边距 **/
    margin: 10px;           /** 设置外边距 **/
    float: left;
    width: 50%;         /** 奇迹发生 **/
    background-color: #CCC;
}
.div1 {
    width: 40%;
    height: 200px;
    margin-bottom: 10px;
    background-color: #0087E6;  /**  这里标注颜色只是为了便于区分块状元素 **/
}
这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。这是一段行内元素。

这是一个左浮动的DIV1

看到没有神奇的一幕发生了,浮动了的行内元素对其设置的宽度、垂直方向的margin都起作用了(而在普通普通流中这些设定对行内元素来说都是无效的),浮动了的行内元素表现得更像是块状元素。

4 浮动带来的麻烦

诚然,引入浮动能够实现更加丰富的布局,但是浮动代码的麻烦也不少:

  1. 元素的浮动会导致顶层容器的塌陷;
  2. 浮动会让内面布局布局变得相对复杂,难以控制(如果你未能较好的掌握浮动的话)。

这里我想补充一下浮动导致顶层容塌陷形成的原因:如果父容器未定义高度,并且子容器进行了浮动且未清除浮动,父容器就无法正确感知到其所包含子容器的高度,最为极端的情况就是所有子容器都进行了浮动并未清除浮动,导致父容器高度为0。

通常我们解决的方法有以下几种(不完全):

  1. 设置父容器,溢出不可见:
    css
    overflow: hideen;

    但是,其也有弊端:所有溢出父容器的内容都会被隐藏掉。
  2. 设置父容器:
    css
    display: table;

    盒模型属性已经改变,可能产生一系列问题,本人未用过该方法。
  3. 清除浮动,这是下文我们需要强调的。

所以,对元素进行浮动后一定要记得清除浮动

5 关于浮动的清除

顺着上文的思路,清除浮动重要性已经说明,清除浮动的CSS代码如下:

clear: left;    /** 清除左浮动 **/
clear: right;   /** 清除右浮动 **/
clear: both;    /** 清除两边浮动 **/ 

清除浮动,看似简单,但事实上还是有那么一点绕头(也就是让人犯晕的意思啦)。

发表评论

电子邮件地址不会被公开。 必填项已用*标注