1.BFC渲染机制
1.1 BFC是什么
Block formatting context(简称 BFC) 块级格式化上下文,这是一个独立的渲染区域,只有块级盒子参与,它规定了内部的块级盒子如何布局,并且和外部不相干。
BFC就是一个管理块级元素的容器
1.2 创建BFC
以下方式会创建BFC:
- 根元素(
<html>
) - 浮动元素(float不是
none
) - 绝对定位元素(position为
abosulte
或fixed
) - 行内块元素(display为
inline-block
) - 表格单元格(display为
table-cell
,HTML表格单元格默认值) - 表格标题(display为
table-caption
,HTML表格标题默认值) - 匿名表格单元格元素( 标签table、row、tbody、thead、tfoot)
- overflow不为
visible
的块元素 - display实际
flow-root
的元素 - contain是
layout
、content
或油漆的元素 - 弹性元素(display为
flex
或inline-flex
元素的子代元素 - 网格元素(display为
grid
或inline-grid
元素的子代元素 - 多列容器(元素的 column-count或 column-width(en-US)不为 为 )auto,包括 column-count1
- column-span 为 all 的元素总是会创建一个新的 BFC,即使元素没有在一个多列容器中(标准变更,铬错误)
1.3 BFC布局
- 内部块级元素独占一行
- BFC不会与浮动盒子重叠(实现自适应的两栏布局)
- 内部的盒子垂直方向的距离由margin决定
- 属于同一个BFC的两个相邻的box的margin会发生重叠(取最大的那个)
- 计算BFC的高度时,浮动元素也参与
- BFC是页面上的一个隔离的独立容器,里面不会受到外界影响(同样不会影响外界)
1.4 BFC特性
特性1:BFC阻止垂直外边距重叠
BFC定义垂直方向距离由margin决定,在同一个BFC内,相邻或嵌套元素间没有隔档(border、padding等) 就会发生margin重叠。
-
相邻兄弟元素margin重叠
只需要在兄弟元素外面包裹一层容器,并触发该容器生成一个BFC。那么两个P便不属于同一个BFC,就不会发生margin重叠了
-
嵌套父子margin重叠
在wrap元素中添加:
overflow:hidden;或者overflow:auto;
使其父元素形成一个BFC;也可以在wrap元素中添加border:1px solid;或是padding:1px;
这些都可以有效解决父子元素margin重叠问题。
特性2:BFC不会重叠浮动元素
利用这个特性可以创造双栏布局
<style>
.box1 {
height: 100px;
width: 100px;
float: left;
background: lightblue;
}
.box2 {
width: 300px;
height: 300px;
background: #eee;
overflow: hidden;
}
</style>
<body>
<div class="box1">我是一个左浮动的元素</div>
<div class="box2">
我是正常盒子
</div>
</body>
特性3:BFC可以包含浮动——清除浮动
元素浮动,脱离了文档流,父容器内容宽度为零(即发生高度塌陷),未能将子元素包裹住。解决这个问题,只需要把把父元素变成一个BFC就行了。常用的办法是给父元素设置overflow:hidden
。
<style>
.box1 {
width: 100px;
height: 100px;
float: left;
border: 1px solid #000;
}
.box2 {
width: 100px;
height: 100px;
float: left;
border: 1px solid #000;
}
.box {
background: yellow;
overflow: hidden;
}
</style>
<body>
<div class="box">
<div class="box1"></div>
<div class="box2"></div>
</div>
</body>
2.垂直水平居中的方式(不定宽高)
2.1 flex 布局
写法够简单直观,兼容性也没什么问题。
利用到了 2 个关键属性:justify-content
和 align-items
,都设置为 center
,即可实现居中。(设置父元素属性,使唯一子元素居中)
.flex-center {
display: flex;
justify-content: center;
align-items: center;
}
2.2 flex + margin
flex 方法的变种;父级元素设置 flex,子元素设置 margin: auto;
。
可以理解为子元素被四周的 margin “挤” 到了中间。
<div class="wrapper">
<p>horizontal and vertical</p>
</div>
<style>
.wrapper {
width: 300px;
height: 300px;
border: 1px solid #ccc;
display: flex;
}
.wrapper > p {
margin: auto;
}
</style>
2.3 transform + absolute
这个组合,常用于图片的居中显示。
<div class="wrapper">
<img src="test.png">
</div>
<style>
.wrapper {
width: 300px;
height: 300px;
border: 1px solid #ccc;
position: relative;
}
.wrapper > img {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
</style>
没有绝对定位,设置img为相对定位,其他属性不变,同样能居中。
2.4 absolute + 四个方向的值相等
使用绝对定位布局,设置 margin:auto;
并设置 top、left、right、bottom 的 值相等即可(不一定要都是 0);一般用于弹出层,需要设置弹出层的宽高。
<div class="wrapper">
<p>hello web</p>
</div>
<style>
.wrapper {
width: 300px;
height: 300px;
border: 1px solid #ccc;
position: relative;
}
.wrapper > p {
width: 170px;
height: 20px;
margin: auto;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
</style>
2.5 table-cell
利用 table 的单元格居中效果展示。
与 flex 一样,需要写在父级元素上。
<div class="wrapper">
<p>hello web</p>
</div>
<style>
.wrapper {
width: 300px;
height: 300px;
border: 1px solid #ccc;
display: table-cell;
text-align: center;
vertical-align: middle;
}
</style>
2.6 writing-mode
该方法通过改变文字的排列方向,让文字先垂直在横向
<div class="wrapper">
<div class="wrapper-inner">
<p>horizontal and vertical</p>
</div>
</div>
<style>
.wrapper {
width: 300px;
height: 300px;
border: 1px solid #ccc;
/* 文字垂直*/
writing-mode: vertical-lr;
text-align: center;
}
.wrapper > .wrapper-inner {
/* 文字横向 */
writing-mode: horizontal-tb;
display: inline-block;
text-align: center;
width: 100%;
}
.wrapper > .wrapper-inner > p {
display: inline-block;
margin: auto;
text-align: left;
}
</style>
2.7 网格布局grid
网格布局让我们能按行或者列来排序,从而能够达到垂直水平居中
兼容性不如flex;IE只支持10以上
<div class="wrapper">
<p>horizontal and vertical</p>
</div>
<style>
.wrapper {
width: 300px;
height: 300px;
border: 1px solid #ccc;
display: grid;
}
.wrapper > p {
align-self: center;
justify-self: center;
}
</style>
2.8 伪元素 ::after
不设置元素的宽度,覆盖高度,元素设置基线的垂直居中
<div class="wrapper">
<img src="test.png">
</div>
<style>
.wrapper {
width: 300px;
height: 300px;
border: 1px solid #ccc;
/* 水平居中 */
text-align: center;
}
.wrapper::after {
content: '';
display: inline-block;
/* 元素基线垂直居中 */
vertical-align: middle;
height: 100%;
}
.wrapper > img {
vertical-align: middle;
}
</style>
2.9 伪元素::before
配合font-size
展开的
<div class="wrapper">
<img src="test.png">
</div>
<style>
.wrapper {
width: 300px;
height: 300px;
border: 1px solid #ccc;
text-align: center;
font-size: 0;
}
.wrapper::before {
display: inline-block;
vertical-align: middle;
font-size: 14px;
content: '';
height: 100%;
}
.wrapper > img {
vertical-align: middle;
font-size: 14px;
}
</style>
3. 深入理解CSS选择器优先级
3.1 什么是选择器优先级
浏览器通过优先级来判断哪一些属性值与一个元素最为相关,从而在该元素上应用这些属性值。优先级是基于不同种类选择器组成的匹配规则。
俗称:权重
3.2 优先级(权重)的计算规则
内联 > ID选择器 > 类选择器 > 标签选择器。
优先级是由 A
、B
、C
、D
的值来决定的,其中它们的值计算规则如下:
- 如果存在内联样式,那么
A = 1
, 否则A = 0
; B
的值等于ID选择器
出现的次数;
C
的值等于类选择器
和属性选择器
和伪类
出现的总次数;D
的值等于标签选择器
和伪元素
出现的总次数 。
<div id="content" class="content">
我是什么颜色
</div>
#content {
color: red;
}
.content {
color: white;
}
套用上面的算法,依次求出 A
B
C
D
的值:
- 因为没有内联样式 ,所以
A = 0
; - ID选择器总共出现了1次,
B = 1
;
- 类选择器出现了1次, 属性选择器出现了0次,伪类选择器出现0次,所以
C = (1 + 0 + 0) = 1
; - 标签选择器出现了0次, 伪元素出现了0次,所以 `D = (0 + 0) = 30;
上面算出的A
、 B
、C
、D
可以简记作:(0, 1, 1, 0)
。
因此颜色是红色
练习一下计算
li /* (0, 0, 0, 1) */
ul li /* (0, 0, 0, 2) */
ul ol+li /* (0, 0, 0, 3) */
ul ol+li /* (0, 0, 0, 3) */
h1 + *[REL=up] /* (0, 0, 1, 1) */
ul ol li.red /* (0, 0, 1, 3) */
li.red.level /* (0, 0, 2, 1) */
a1.a2.a3.a4.a5.a6.a7.a8.a9.a10.a11 /* (0, 0, 11,0) */
#x34y /* (0, 1, 0, 0) */
li:first-child h2 .title /* (0, 0, 2, 2) */
#nav .selected > a:hover /* (0, 1, 2, 1) */
html body #nav .selected > a:hover /* (0, 1, 2, 3) */
3.3 优先级的特殊情况
内联样式的优先级是最高的,但是外部样式有没有什么办法覆盖内联样式呢?有的,那就要 !important
出马了。因为一般情况下,很少会使用内联样式 ,所以 !important
也很少会用到!如果不是为了要覆盖内联样式,建议尽量不要使用 !important
。
.box { max-width: 100px;}
#box { width: 100px !important;}
使用!important
踩的坑
.box
的宽度只有 100px
, 而不是 300px
, 可见,max-width
可以超越 width!important
!但是,这实际上不是优先级的问题,因为优先级是比较相同属性的,而 max-width
和 width
是两个不同的属性。
优先级是同属性相互比较的
4. 清除浮动的多种方式
浮动的方式进行布局,这样就会使元素脱离文档流。而在文档流中,父元素会默认被子元素撑开,一旦子元素设置浮动,完全脱离文档流,就会导致子元素无法撑起父元素的高度,就会产生父元素高度塌陷问题。
4.1 开启BFC
BFC的特性:BFC可以包含浮动元素,浮动元素也参与高度计算
将父级元素变成BFC块级上下文(参考上面BFC内容),就能够解决该问题
代码示例
.parent-box {
width: 200px;
background: gray;
border: 5px solid;
overflow: hidden;
zoom: 1;
}
4.2 父元素后面添加一个空的div
解决高度塌陷问题最简单的方法,直接在高度塌陷的父元素里最后面添加一个空的div,并将设置clear :both 属性,这样由于这个空的div并没浮动,所以可以撑开父元素高度,基本没有什么副作用。但是会在页面中添加多余的结构,不建议使用。
<body>
<div class="parent-box">
<div class="son-box"></div>
<div style="clear: both;"></div>
</div>
<div class="another-box"></div>
<body>
4.3 给父元素设置高度
设置高度不能适应页面的快速变化;另外一种,父容器的高度可以通过内容撑开(比如img图片),实际当中此方法用的比较多。
4.4 ::after伪元素清除浮动(推荐)
不能适应页面的快速变化;另外一种,父容器的高度可以通过内容撑开(比如img图片),实际当中此方法用的比较多。
和第二种方式原理一样,但不会产生多余结构
.parent-box {
width: 200px;
background: gray;
border: 5px solid;
}
.parent-box::after {
content: "";
display: block;
clear: both;
}
4.5 父元素内添加br标签
br标签存在一个属性clear,在br标签中设置属性clear="all"
,即能清除浮动。但这种方法与第二种一样,同样会使页面结构冗余。
<body>
<div class="parent-box">
<div class="son-box"></div>
<br clear="all" />
</div>
<div class="another-box"></div>
<body>