在 CSS 中,浮动(float)是一种用于控制元素布局的属性。在 CSS 基础中浮动是必学的概念,同时 CSS 浮动也因为其产生的奇怪行为而难以为前端初学者所理解。本文从 CSS 浮动的最本质应用场景——文本环绕出发,延伸到元素浮动,探讨 CSS 浮动的原理和特点,最后对其产生的影响进行讲解及解决。
文本环绕
CSS 浮动最初以及最常用的一个场景就是让文本环绕着图片,所以先从文本环绕这个案例来讲解 CSS 浮动。
如下 HTML 结构中有一个img图片,以及一些文本内容:
<div>
<img/>
text text text text
text text text text
text text text text
text text text text
text text text text
text text text text
text text text text
text text text text
text text text text
</div>
给元素添加一些基本的样式,大的背景是灰色的,图片用橙色表示:
div {
width: 300px;
background-color: gray;
}
img {
width: 100px;
height: 100px;
background-color: orange;
}
此时页面展示如下,因为img是行内块元素所以与文本并列,且由于基线对齐的原因第一行文本从图片底部开始对齐:
如果要让文字围绕方块,则需要给img元素添加浮动,即float: left:
img {
width: 100px;
height: 100px;
background-color: orange;
float: left;
}
文本环绕的原理与理解
在文本环绕这个案例中,给img元素添加loat: left即可让文字环绕图片排版,这是怎么做到的?
- 首先
loat: left会让浮动元素向左移动,直到碰到包含框(父元素)的边缘或其他浮动元素。 - 浮动元素后面的非浮动元素会围绕着浮动元素进行布局。
- 浮动元素的宽高不再自动撑满包含框(父元素),而是根据其内容的宽高来确定,这时父元素的高就是由文本决定的。
那如何理解浮动这样一种现象:
- 包含框(父元素)可以理解为一个无限大(如果宽高不定义)的池塘。
- 图片浮动起来,就像是池塘中的浮萍。
- 文本就是池塘中的水流,环绕着浮萍。
- 水流的多少会决定池塘的大小,而如果池塘(宽高)足够小可能就容不下浮萍。
元素浮动
对文本环绕有个印象后,再接着对元素浮动展开讲解。
如下 HTML 结构中有 3 个div块元素:
<div id="d1">d1</div>
<div id="d2">d2</div>
<div id="d3">d3</div>
给这些块元素添加一些样式作区分:
div {
padding: 10px;
height: 50px;
background-color: gray;
}
#d1 {
background-color: lightpink;
}
#d2 {
background-color: lightblue;
}
#d3 {
background-color: lightgreen;
}
此时页面的呈现如下:
接下来尝试让蓝色#d2浮动,添加float: left:
#d2 {
background-color: lightblue;
float: left;
}
来看一下页面,分析产生了什么变化:
- 首先
#d2缩小了,这是因为它没有指定宽度,成为浮动元素后宽度由内容决定。 - 其次
#d2与#d3并列了,这是因为非浮动元素会围绕着浮动元素进行布局。
让#d2做一些偏移,加深理解浮动的含义:
#d2 {
background-color: lightblue;
float: left;
margin-top: 10px;
margin-left: 50px;
}
来看一下页面,分析浮动的现象:
- 首先就是
#d3它是个完整的元素,宽高也没有因为#d2而被压缩,这就是因为浮动元素会脱离文档流。 - 其次是
#d3中的文本它会随着#d2同时横向偏移,因为浮动最初就是用作于文本环绕。
元素浮动后的特点
到这里可以总结一下元素浮动后的特点:
- 脱离文档流,简单讲就是不再在页面布局中占据原来的空间,后面的元素会无视它的存在进行布局。
- 元素浮动后默认宽高将重新由内容区决定,也可以设置宽高。
- 块元素不会独占一行了。
- 不会产生 margin 塌陷合并问题。
CSS浮动产生的影响
CSS 浮动可以解决一些排版问题或者 CSS 中 margin 塌陷问题,但是它也会产生一些预期之外的影响。
如下 HTML 结构中,首先是未浮动前的样式:
<div id="outer">
<div class="d d1">d1</div>
<div class="d d2">d2</div>
<div class="d d3">d3</div>
</div>
给这些元素添加一些样式作区分:
#outer {
width: 300px;
background-color: gray;
border: 5px dashed black;
}
.d {
width: 50px;
height: 50px;
margin: 10px;
}
.d1 {
background-color: lightpink;
}
.d2 {
background-color: lightblue;
}
.d3 {
background-color: lightgreen;
}
页面的展示如下:
给所有的元素都添加浮动:
.d1,.d2,.d3 {
float: left;
}
此时会看到,因为.outer中的元素都浮动了,使得其中没有内容,因此没有高度,只可以看到 border:
继续添加一个class="d d4"元素,给它一个背景色作区分,但是不设置它浮动:
<div id="outer">
<div class="d d1">d1</div>
<div class="d d2">d2</div>
<div class="d d3">d3</div>
<div class="d d4">d4</div>
</div>
.d4 {
background-color: lightseagreen;
}
来分析一下页面中产生的变化:
- 会看到
class="d d4"内容区的文本环绕着.outer。 - 而
.outer是被.d4填充了高度。 - 看不到
.d4是因为被.d1遮挡了。
为了让.d4显示出来,需要清除因为前后元素浮动带来的影响:
.d4 {
background-color: lightseagreen;
clear: both;
}
页面展示如下:
不过在一些布局场景中,通常是需要这些块元素并列,继续尝试让.d4与前面的元素同一行内排列:
.d4 {
background-color: lightseagreen;
clear: both;
display: inline-block;
}
页面展示如下:
当然还有更好的方法,因为不可能每添加一个新的块元素都单独写一次样式,所以可以用伪元素选择器实现:
- 给外层的包含框(父元素)添加一个伪元素选择器
::after。 - 给伪元素添加空内容,并清除前后浮动元素影响。
- 记得给伪元素添加
display: block这样父元素才能有块元素而撑开。
.d1,.d2,.d3,.d4 {
float: left;
}
#outer::after {
content: '';
clear: both;
display: block;
}