目录
CSS将所有的元素都当成盒子,CSS布局其实就是如何堆放盒子。
在说浮动布局之前,我们需要了解标准布局,所谓标准布局,即按照元素在标准流中的特点布局
这种布局方式中
所以,在标准布局时代,我们多基于table进行网页布局。但是table布局也有非常严重的缺点:
其实,本质上来看,标准布局的短板在于其水平方向上的布局只能使用行内、行内块元素,而行内、行内块元素由于各种问题,并不适用于布局,只适用于作为内容。
而块级元素并不存在行内和行内块元素的这些问题,所以如果块级元素也能用于水平方向布局,那么就完美了。
标准布局中的元素处于标准流中。
我们可以暂且将标准流理解为PS中的图层。标准流就是网页最底层的图层。
为了解决标准布局中,块级元素不能在水平方向布局的问题,即多个块级元素不能共享一行的问题,CSS提出了浮动的概念。
所谓浮动,即为元素设置float样式,float样式有如下值:
一旦,元素设置了float:left或者float:right,则元素就会脱离标准流,简称“脱标”,然后进入浮动流。
所谓浮动流,我们可以理解其为处于标准流图层之上的另一个图层。
而一旦元素脱标后,元素就会释放其在标准流中的占据的位置,这将导致标准流中脱标元素之后的元素前据。
在浮动流中,多个块级元素是可以共享一行的
上例中,box1~box3设置了float:left,所以它们脱标进入浮动流,并共享一行,而box1~box3脱标后,它们在标准流中的位置就会被释放,此时box4这个依旧在标准流中的元素就会进据被释放的位置。
所以上例中,box4实际上有一部分被box1~bxo3遮盖住了,因为浮动流在标准流之上。
一旦标准流中元素被加了float:left或float:right样式,则元素就变为了浮动元素,脱标进入浮动流。
浮动元素其实可以看成是:特殊的行内块元素,它具备行内块的部分特点
标准流中的行内元素、行内块元素、块级元素加了浮动后,都会变成浮动元素,具备以上浮动元素的特性。
浮动方向?
浮动排列?
浮动范围?
浮动元素的浮动范围受到其父级元素,和兄弟元素的影响:
浮动元素的浮动范围只能在其父级元素内部(无论父级在标准流还是在浮动流),并且浮动元素不会压住父级元素的border、padding,父级元素content中的行内元素、行内块元素的内容会自动围绕浮动元素排列
浮动元素本身就处于父级元素的content中,如果浮动元素存在兄弟元素:
如果兄弟元素也是浮动元素,则
前面的兄弟先浮动,后面的兄弟后浮动。
如果兄弟元素是非浮动元素,则
浮动元素不会上浮压住前面的非浮动兄弟元素,
而浮动元素脱标,会释放占据的标准流位置,所以浮动元素后面的非浮动兄弟会进据释放的位置,从而被浮动元素压住。
总结:浮动元素只会影响浮动元素后面的标准流,不会影响前面的标准流
一般而言,我们不给标准流中父级容器设定height,而是让容器height自动适配为容器内容的高度,但是如果,此时有一个子级元素发生了浮动,则标准流中的父级容器的高度会发生塌陷
原因是:标准流中父级容器的高度本来是由标准流中子级元素撑开的,但是当子级元素脱标后,对应标准流中的位置就会被释放,所以标准流中父级容器就没有内容了,此时父级容器的height就为0了。
清除浮动是为了解决:当子级元素浮动后,其标准流中高度自适应的父容器高度塌陷问题
方案1:让标准流中父容器变为BFC模式,这样就可以保证父容器中子元素渲染不会影响外界了(即:不会引起父容器高度变化)
前面学习将元素转为BFC渲染模式的方式有:
方案2:利用clear样式来清除浮动引起的父容器高度塌陷
在学习clear样式清除浮动影响前,我们要先了解clear样式
首先:
clear样式只能加给块级元素。对行内元素、行内块元素设置clear样式无效。
clear样式既可以加给浮动元素,也可以加给非浮动元素。
clear样式有三种常用值:
如果给浮动元素加clear样式,则含义是:
box1,box2都是左浮动元素。
如果box2不加clear:left,理论上box1,box2应该左浮动到一行。
但是box2加了clear:left,所以要保证box2左侧无浮动元素,所以只能让box2换行排列。
box1是右浮动元素,box2是左浮动元素。
如果box2不加clear:right,则box1,box2应该在一行,并且box2在左侧,box1在右侧。
但是box2加了clear:right,所以要保证box2右侧无浮动元素,所以只能让box2换行排列。
box1是右浮动元素,box2、box3是左浮动元素。
如果box3不加clear:both,则box1,box2,box3应该排列在一行。
但是box3加了clear:both,所以要保证box3两端无浮动元素,所以只能让box3换行。
另外,我们需要注意下面这种情况
这里box1是右浮动元素 ,box2,box3是左浮动元素。
我们给box2加了clear:both,但是box2却依然和box3排列在一行,而没有保证box2右侧无浮动元素。
我个人理解原因应该是:浏览器按照从上到下顺序,依次渲染每个DOM元素的,当渲染到box2时,执行了clear:both,但是此时box2只有前面的box1右浮动元素,后面的box3还没有被渲染,所以此时clear:both只需要保证box2右侧无浮动元素即可,让其换行,然后clear:both工作结束。
之后,box3被渲染出来,进行左浮动。
所以我们应该将设置了clear样式的元素放在容器尾部。
如果给非浮动元素加clear样式,则含义是:
clear:left | 清除当前元素前面的,左浮动元素脱标后带来的影响(高度塌陷) |
clear:right | 清除当前元素前面的,右浮动元素脱标后带来的影响(高度塌陷) |
clear:both | 清除当前元素前面的,左、右浮动元素脱标后带来的影响(高度塌陷) |
可以发现,
非浮动元素上clear:left
同理,非浮动元素上clear:right也是如此。
而clear:both既可以清除左浮动元素脱标的影响,也可以清除右浮动元素脱标的影响。
另外需要注意的是,加了clear样式的元素只能清除位于它前面的浮动元素的影响,无法清除它后面的浮动元素的影响。
所以我们需要保证加了clear样式的元素在父容器尾部。
因此,为了清除浮动带来的父容器高度塌陷影响,我们可以使用如下方案:
额外标签法:在父容器内容最后新增一个块级标签,并为其设置clear:both
::after伪元素法:为父容器新增一个::after伪元素,设置其显示模式为block,并为其设置clear:both
其中额外标签法会引入无关标签,破坏原有DOM结构,而::after伪元素法,不会引入无关标签,不会破坏DOM结构。
还有一种双伪元素法,即给父容器添加 ::before,::after伪元素,并在这两个伪元素上设置clear:both,以形成严格的浮动闭合。
但是我觉得有点多次一举了,因为clear:both只对它前面的浮动元素有效果,对后面的浮动元素无效果,所以::before伪元素清除浮动几乎没有任何效果。
浮动布局最出名的就是:圣杯布局和双飞翼布局。
圣杯布局和双飞翼布局,都属于三栏式布局,且都是两侧边栏宽度固定,中间内容区宽度自适应。
目前来看实现起来非常容易,借助CSS的calc函数和浮动即可:
- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Documenttitle>
- <style>
- html, body {
- margin: 0;
- padding: 0;
- }
-
- .container {
- width: 100%;
- }
-
- .container::after {
- content: "";
- display: block;
- clear: both;
- }
-
- .left-aside, .right-aside {
- width: 200px;
- height: 200px;
- float: left;
- }
-
- .left-aside {
- background-color: red;
- }
-
- .right-aside {
- background-color: blue;
- }
-
- .content {
- width: calc(100% - 400px);
- height: 200px;
- background-color: green;
- float: left;
- }
- style>
- head>
- <body>
- <div class="container">
- <div class="left-aside">div>
- <div class="content">div>
- <div class="right-aside">div>
- div>
- body>
- html>
但是圣杯和双飞翼都是上古时代的布局方式,那时候CSS还没有calc函数,所以实现起来要更加困难一点。
在上古时代,前端大能们巧妙地利用了margin负值来实现圣杯和双飞翼布局。
下面是圣杯布局的实现代码:
- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Documenttitle>
- <style>
- html, body {
- margin: 0;
- padding: 0;
- }
-
- .container {
- padding: 0 200px; /* 预留出左右padding,给left-aside,right-aside */
- }
-
- .container::after {
- content: "";
- display: block;
- clear: both;
- }
-
- .left-aside, .right-aside {
- float: left;
- width: 200px;
- height: 200px;
- }
-
- .left-aside {
- background-color: red;
- margin-left: -200px; /* 左移200px */
- }
-
- .right-aside {
- background-color: blue;
- margin-right: -200px; /* 右移200px */
- }
-
- .content {
- float: left;
- width: 100%;
- height: 200px;
- background-color: green;
- }
- style>
- head>
- <body>
- <div class="container">
- <div class="left-aside">div>
- <div class="content">div>
- <div class="right-aside">div>
- div>
- body>
- html>
效果和上面calc函数实现的效果一致。
这里内容区宽度自适应的逻辑是:
父容器不设置宽度,但是设置左右padding,左右padding的大小等于侧边栏的固定宽度。
父容器的内容区的width占满剩余宽度,即100%。
然后我们先将红色的left-aside向左移动200px,即margin-left:-200px
此时,发生了两件事:
为什么content会再次向上浮动一行呢?
答案肯定是:顶部行的剩余宽度足够容纳content了
但是content宽度现在是是100%,自动占满剩余宽度,也就是说left-aside宽度为0了,content才有机会向上浮动一行。
那么为啥left-aside宽度为0呢?
其实这里说的left-aside宽度为0,
并不是指left-aside盒子的width为0,
而是指left-aside盒子的width+ 左右padding + 左右border + 左右margin = 0
有可能吗?
有,前面我们说过,margin和width、padding、border最大的区别在于:margin支持负值。
left-aside盒子的width+ 左右padding + 左右border + 左右margin = 0
是完全有可能的,我们看看当前left-aside盒子的实际数据:
则它们的和为0。
所以同理,我们设置right-aside盒子的margin-left或margin-right也为-200px,就能让right-aside盒子的整体宽度为0,然后right-aside盒子也能继续上浮。
假设,我们设置right-aside盒子的margin-left为-200px
发现出问题了,蓝色的right-aside盒子虽然上浮了(因为整体宽度变为0),但是却没有覆盖父容器的padding-right区域,而是覆盖了content,这是为什么呢?
我们可以看下right-aside的盒子模型
可以发现盒子左侧margin-left:-200px和盒子内容width:200px抵消了
假设,right-aside盒子只margin-left:-199px,则此时right-aside盒子只被抵消了199px的盒子内容宽度,则还剩1px的内容宽度。
再假设,content所在行,还剩1px的剩余宽度,则right-aside盒子就可以上浮,并且肯定将自身1px有效区域放到content所在行剩余宽度1px中,而right-aside盒子其余199px无效区域只能覆盖在content身上。
上面都理解了的话,我们可以继续抽象出一个0px的有效区域,200px的无效区域的right-aside盒子 放入了 content所在行的剩余宽度0px中。
到此,就完成了right-aside盒子为啥margin-left:-200px后,会压在content盒子上了。
所以我们应该很容易理解right-aside盒子margin-right:-200px后的样子
此时right-aside盒子的0px有效区域,被上浮到了content所在行的0px剩余宽度中,则right-aside盒子的200px无效区域自然被压倒了父容器padding-right区域。
下面是双飞翼布局的实现代码:
- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Documenttitle>
- <style>
- html, body {
- margin: 0;
- padding: 0;
- }
-
- .container {
- float: left;
- width: 100%;
- }
-
- .container::after {
- content: "";
- display: block;
- clear: both;
- }
-
- .left-aside, .right-aside {
- float: left;
- width: 200px;
- height: 200px;
- }
-
- .left-aside {
- margin-left: -100%;
- background-color: red;
- }
-
- .right-aside {
- margin-left: -200px;
- background-color: blue;
- }
-
- .content {
- padding: 0 200px;
- height: 200px;
- background-color: green;
- }
- style>
- head>
- <body>
- <div class="container">
- <div class="content">div>
- div>
- <div class="left-aside">div>
- <div class="right-aside">div>
- body>
- html>
双飞翼布局和圣杯布局的表现效果相同,并且实现原理也一样,都是依赖于float + margin负值,二者的区别在于双飞翼的布局结构更复杂一点。