揭开css中浮动的本质以及怎么清除浮动

207 阅读13分钟

在css中有许多布局方式,如:浮动布局、定位布局、弹性布局、网格布局和表格布局。今天我们来聊css中的浮动布局。

1. 浮动存在的意义

为什么会有浮动这个概念呢?它是用来干什么的?我们先通过一个例子来认识一下浮动。

<body>
    <div class="box">
        <img width="100" height="100"
            src="https://img1.baidu.com/it/u=3610006291,1844666164&fm=253&fmt=auto&app=120&f=JPEG?w=800&h=800" alt="">
        <p>今年是中巴建交50周年,中巴关系已成为发展中大国团结合作、携手发展的典范
        。“中国和巴西同为发展中大国和重要新兴市场国家,是志同道合的好朋友、携手前行的好伙伴。”
        志合者,不以山海为远。在此次拉美之行中,习近平主席将应邀对巴西进行国事访问。
        在元首外交引领下,中国将同巴西进一步巩固双方政治互信,加强两国发展战略对接,
        深化双方在全球热点问题上的战略沟通和协调,携手开启中巴关系下一个“黄金50年”。</p>
    </div>
</body>

我们在html里添加一个类名为box的盒子,里面放了一张图片,然后在图片的下面放了一个p标签,里面放了一段文字。我们来看一下效果。

image.png

图片在左上角,而文字在图片下面。因为p标签是块级标签嘛,它要占据一整行。

那我们想让图片和文字出现在同一行应该怎么办呢?让文字环绕图片,在网页中很常见的结构嘛。

浮动就能发挥这个作用。我们给img标签写个样式,让它浮动起来,用float来解决。

 <style>
 img {
     float: left;
        }
 </style>

float: left就是让图片向左浮动。这是我们来看看效果。

image.png

这样文字就去到图片的右边了让图片浮动了起来。

这就是浮动存在的意义:让文字能环绕图片。在当年的互联网时代,网页布局都很简陋,多是新闻网页,当时的人们就是想让图片和文字能出现在同一行,既美观又节省空间。所以就发明了浮动。

那为什么浮动会出现这种效果呢?

我们换种场景,我们不往图片下面添加文字,我们添加一个类名为red的盒子,给它增加一些样式,然后照样让图片浮动起来。我们来看看效果。

<body>
    <div class="box">
        <img width="100" height="100"
            src="https://img1.baidu.com/it/u=3610006291,1844666164&fm=253&fmt=auto&app=120&f=JPEG?w=800&h=800" alt="">
        <p>
        <div class="red"></div>
        </p>
    </div>
</body>
<style>
        img {
            float: left;

        }

        .red {
            width: 200px;
            height: 200px;
            background-color: red;
        }
    </style>       

image.png

我们发现图片是不是和盒子重叠了呀,和上面的图片和文字呈现的效果有所不同。图片并没有盖住文字。而这里图片却盖住了盒子。这是为什么呢?

我们得来引入一个文档流的概念。什么叫文档流呢?文档流是浏览器默认的一种布局方式。

1. 从上往下,从左往右的布局排列,遵从标签的特性

标签的特性:

1.块级元素会独占一行,从上向下顺序排列。

•常用元素:div、 hr、p、hl~ho、ul、 ol、 dl、 form、table

2.行内元素会按照顺序,从左到右顺宇排列,碰到列父元素边缘则自动换行。

•常用元素:span、a、i、 em等

2. 浮动会导致元素脱离文档流

3. 文字一定不会被浮动元素盖住

所以图片能盖住盒子,而不能盖住文字。因为添加了浮动,导致了这个元素脱离了文档流,它就不能独占一行了。

那我们一般怎么去使用浮动呢?举个例子,我们写一个ul里面3个li,然后给它们写点样式,各自加一个背景颜色。

<body>
    <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
    </ul>
</body>
<style>
        ul {
            list-style: none;
            margin: 0;
            padding: 0;
        }

        li {
            width: 200px;
            height: 50px;
        }

        li:nth-child(1) {
            background-color: red;
        }

        li:nth-child(2) {
            background-color: yellow;
        }

        li:nth-child(3) {
            background-color: green;
        }
    </style>

我们来看一下输出结果:

image.png

输出结果显示每一个li独占一行,因为它是块级元素。

那我们想让这三个li去到同一行怎么做?

你肯定脱口而出用浮动呀,我们就是在聊浮动。

我们先不用浮动,有没有其它的办法让它们去同一行?

因为li是块级元素,我们把它转变成行内块元素就行了。

<style>
li {
            width: 200px;
            height: 50px;
            display: inline-block;
        }
</style>

输出结果显示:

image.png

确实到一行去了,但我们发现,每个li之间有一点小空隙,这个空隙是怎么来的?其实就是因为我们在写li的时候换行导致的。在浏览器渲染的时候,它会把换行处理成一个空格,所以会有间隙。

那我们不想要这个间隙,怎么去除它呢?我们这样去除。

<style>
        ul {
            list-style: none;
            margin: 0;
            padding: 0;
            font-size: 0;
        }

        li {
            width: 200px;
            height: 50px;
            display: inline-block;
            font-size: 16px;
        }
    </style>

因为换行被看出了一个字符,那么我们只要让ul中的字符大小设置成0就行了,而因为我们在li中要输出1、2、3,所以再在li上再设置字符大小为16px就行了。这样ul中的空白字符就会消失,而li中的字符正常显示。

image.png

这样就让间隙消失了。

2. 浮动的负面影响

我们不用上面那种方法。我们来使用浮动去完成上面的效果,这会导致一个负面效果。我们来看。

<style>
        ul {
            list-style: none;
            margin: 0;
            padding: 0;
        }

        li {
            width: 200px;
            height: 50px;
            float:left;
        }
    </style>

我们给3个li都添加向左浮动,在看结果之前,我们思考一下,我们没设置浮动之前,3个li是上下排列的,我们给li设置了高度50px,没有给ul设置高度,所以ul会被li撑开,高度应为150px。此时我们给li添加了浮动,它们去到了同一行,这时父容器ul的高度会是多少呢?

我们在浏览器中检查一下。

image.png

我们发现ul的高度变为0了。因为此时li添加了浮动导致脱离了文档流,没办法去撑开父容器ul,所以ul的高度就为0。

我们说的负面影响就在这体现。

我们在ul后面再写一个box,给他添加一些样式。宽为500px,高为100px。

<body>
    <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
    </ul>
    <div class="box"></div>
</body>
<style>
        ul {
            list-style: none;
            margin: 0;
            padding: 0;
        }

        li {
            width: 200px;
            height: 50px;
            float: left;
        }

        li:nth-child(1) {
            background-color: red;
        }

        li:nth-child(2) {
            background-color: yellow;
        }

        li:nth-child(3) {
            background-color: green;
        }
        .box {
            width: 500px;
            height: 100px;
            background-color: #f007d5;
        }
    </style>

我们来看一下输出结果。

image.png

我们发现新添加的这个盒子被盖住了。因为ul没高度了,所以这个盒子就从左上角开始排列。

这就是浮动会导致的负面影响:浮动元素的高度不计算在父容器的高度之内,这就会导致父容器的后续容器布局和该浮动元素重叠

而在我们日常工作中,我们写了一个盒子,肯定不希望它被上面的盒子盖住,有什么办法能去除这种影响吗?

3. 清除浮动

想要去除这种负面影响,就得聊到清除浮动了。

第一种方法,既然你是因为没有高度导致后续容器重叠,我直接给你设个高度就行了。我们给父容器ul设置高度为50px。来看看效果。

<style>
        ul {
            list-style: none;
            margin: 0;
            padding: 0;
            height:50px
        }

        li {
            width: 200px;
            height: 50px;
            float: left;
        }

        li:nth-child(1) {
            background-color: red;
        }

        li:nth-child(2) {
            background-color: yellow;
        }

        li:nth-child(3) {
            background-color: green;
        }
        .box {
            width: 500px;
            height: 100px;
            background-color: #f007d5;
        }
    </style>

image.png

确实得到了我们想要的样子,没有重叠。但这种方法我们一般不使用,因为在实际开发中,我们就是想让子容器撑开父盒子,不会给父容器增加高度,因为有时候子容器是一直向下延伸的,所以一般我们无法得知父容器的高度。

第二种方法,我们在在浮动元素最末尾增加一个子容器,在子容器上做清除浮动。例如:

<body>
    <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <div class="clear"></div>
    </ul>
    <div class="box"></div>
</body>
<style>
        ul {
            list-style: none;
            margin: 0;
            padding: 0;
        }

        li {
            width: 200px;
            height: 50px;
            float: left;
        }

        li:nth-child(1) {
            background-color: red;
        }

        li:nth-child(2) {
            background-color: yellow;
        }

        li:nth-child(3) {
            background-color: green;
        }
        .box {
            width: 500px;
            height: 100px;
            background-color: #f007d5;
        }
        .clear {
            clear: both
        }
    </style>

我们在ul的末尾加一个叫clear的盒子,给它写一个样式,clear: both、clear: left、clear: right,都行,清除浮动,将浮动的效果终止在这个盒子上,就不会影响到父盒子了。这样也能达到我们想要的效果。

屏幕截图 2024-11-20 093808.png

但这种方法我们也一般不使用,因为每多一处我们都得添加一个清除浮动的盒子,浪费代码空间。

第三种方法,添加伪元素。我们在ul上添加伪元素,然后在伪元素上清除浮动。伪元素有两种,before和after,用哪种能清除浮动呢?我们来试一下。

<style>
        ul {
            list-style: none;
            margin: 0;
            padding: 0;
        }

        li {
            width: 200px;
            height: 50px;
            float: left;
        }

        li:nth-child(1) {
            background-color: red;
        }

        li:nth-child(2) {
            background-color: yellow;
        }

        li:nth-child(3) {
            background-color: green;
        }
        .box {
            width: 500px;
            height: 100px;
            background-color: #f007d5;
        }
        ul::before {
            content: "";
            clear: both;
            display: block;
        }
        
    </style>

我们在ul上添加了伪元素,把它转换成块级元素,然后清除浮动。注意,伪元素里一定要有‘content’属性。我们来看看效果。

image.png

好像并没有效果,box盒子还是被盖住了。这是因为before伪元素一定会出现在ul元素的最前面,所以也会在li前面,而我们是在li上添加的浮动,所以自然没有效果。

我们再来试一下after伪元素。

<style>
        ul {
            list-style: none;
            margin: 0;
            padding: 0;
        }

        li {
            width: 200px;
            height: 50px;
            float: left;
        }

        li:nth-child(1) {
            background-color: red;
        }

        li:nth-child(2) {
            background-color: yellow;
        }

        li:nth-child(3) {
            background-color: green;
        }
        .box {
            width: 500px;
            height: 100px;
            background-color: #f007d5;
        }
        ul::after {
            content: "";
            clear: both;
            display: block;
        }
        
    </style>

我们来看一下效果。

image.png

我们发现after伪元素成功清除了浮动。

伪元素清除法就是我们常用的方法。

还有第四种方法其实我们也不推荐使用。我们一起来看一下。

第四种方法就是给受影响的容器添加清除浮动。什么意思呢?我们添加的box盒子不是被盖住了嘛,那我们给这个受影响的box盒子添加浮动也能达到这个效果。

<style>
        ul {
            list-style: none;
            margin: 0;
            padding: 0;
        }

        li {
            width: 200px;
            height: 50px;
            float: left;
        }

        li:nth-child(1) {
            background-color: red;
        }

        li:nth-child(2) {
            background-color: yellow;
        }

        li:nth-child(3) {
            background-color: green;
        }
        .box {
            width: 500px;
            height: 100px;
            background-color: #f007d5;
            clear: both;
        }
    </style>

我们给box盒子添加了clear: both,我们来看看效果。

屏幕截图 2024-11-20 131600.png

也成功去除了浮动。所以总结,清除浮动的方法一共有四种:

1. 直接设置父容器的高度

2. 在浮动元素最末尾增加一个子容器,在子容器上做清除浮动

3. 添加伪元素(最常用)

4. 给后面被影响的元素添加清除浮动

4. BFC容器 ---Block Formatting Contezt

其实我们还有一种最推荐的方法,就是BFC容器。我们看看它是怎么处理的。

BFC容器是什么呢?它是一个拥有隔离空间的容器

任何标签都能称为BFC容器,就是拥有块级格式上下文的容器。

它被创造出来是干什么的呢?

我们通过一个例子来认识一下它。

在html中有一个经典的的bug。

<body>
    <div class="parent">
        <div class="child"></div>
    </div>
</body>
<style>
        * {
            margin: 0;
            padding: 0;
        }
        
        .parent {
            width: 100vw;
            height: 400px;
            background-color: purple;
        }

        .child {
            width: 100vw;
            height: 200px;
            background-color: blue;
            margin-top: 50px;
        }
    </style>

我们添加了一个父盒子,里面又添加了一个子盒子,给他们添加颜色、高度。然后我们给子容器添加一个外边距20px。 按理来说。子盒子添加了外边距20px是不是就应该离父盒子的上方有20px的距离。我们来看一下效果:

image.png

我们发现子盒子并没有离父盒子的上方有20px外边距,而是紧紧贴着父盒子的上方,并且把整个body拉下来了20px。这就是这个bug,而我们并不想要这个效果,该怎么去解决它呢?

这就是BFC容器存在的意义了,它一开始被设计出来就是为了解决父子容器margin重叠的问题

那把谁设计成BFC容器呢?我们给父盒子添加一个样式:overflow: auto;

<style>
        * {
            margin: 0;
            padding: 0;
        }
        
        .parent {
            width: 100vw;
            height: 400px;
            background-color: purple;
            overflow: auto;
        }

        .child {
            width: 100vw;
            height: 200px;
            background-color: blue;
            margin-top: 50px;
        }
    </style>

我们把父容器改造成了BFC容器,我们来看看效果:

image.png

我们成功达到了我们想要的效果,父盒子里面的子盒子离父盒子上边距有20px,并且body也没有被拉下来20px。

除了上面那种方法,还有哪些方法能把容器设置成BFC容器呢?有下面几种:

创建BFC 的方法

  1. overflow:hidden || auto || overlay || scroll
  2. 定位: position: absolute || fixed
  3. display: inline-xxxx || flex || grid
  4. display: table || table-xxx
  5. 浮动:float: left || right

大家可以自行去试一下。只要把父容器设置为BFC容器,它就能变成一个拥有独立空间的容器,它里面的子元素就不会受影响,就能解决这个bug。

5. BFC容器可以用来清除浮动

那聊了这么多关于BFC容器的概念,它对于解决清除浮动有什么妙用呢?

我们回到我们ul里有3个li的例子,我们把ul设置为BFC容器看看。

<style>
        ul {
            list-style: none;
            margin: 0;
            padding: 0;
            overflow: hidden;
        }

        li {
            width: 200px;
            height: 50px;
            float: left;
        }

        li:nth-child(1) {
            background-color: red;
        }

        li:nth-child(2) {
            background-color: yellow;
        }

        li:nth-child(3) {
            background-color: green;
        }
        .box {
            width: 500px;
            height: 100px;
            background-color: #f007d5;
            clear: both;
        }
    </style>

我们给父容器ul添加了一行属性:overflow: hidden;,这行代码可以把容器设置为BFC容器。现在我们来看看效果。

屏幕截图 2024-11-20 131600.png

同样使box盒子没有和ul重叠,说明我们成功去除了浮动。

所以,只要把父盒子设置成BFC容器,它里面的子盒子添加浮动就不会有负面影响了。

所以BFC的特性:

  1. BFC容器是让处于BFC内部的元素与外部的元素相互隔离。使外部元素的定位不会相互影响。
  2. 解决外边距重叠问题
  3. BFC容器在计算高度时,会将浮动元素的高度也计算在内(解决浮动的负面影响)