共计 16518 个字符,预计需要花费 42 分钟才能阅读完成。
我想你每天写 css 代码有时候也会觉得很痛苦:这个布局的 css 怎么这么难实现!我也经常会有这种感觉,一个看似简单的布局总是要琢磨半天才能实现,偶尔还会出现一些怪异的超出理解的现象。这是因为我们对 css 只是大概知道个形,并没有看透 css 的本质。在同事的推荐下我阅读了张鑫旭老师的《css 世界》,才发现 css 跟想象中的不太一样。本文为《css 世界》个人总结笔记,为缩减篇幅丢弃了张老师冗余的小幽默,丢掉了些含金量较低的章节内容,因为 ie 已经被淘汰出局,所以有关 css 兼容性的地方也全部忽略不记,同时对个人觉得不易理解的地方加上了一些自己的理解和验证,所以错误之处还望指正。顺便推荐个好用的在线代码编辑工具,国内镜像站点,方便各位对本文实例进行测试。另外本文会随着作者对 css 的更深入理解而逐步更新,希望到最后能够文如标题展现出真正的 css 世界。
小刚老师
- 基本概念
- 盒模型四大金刚
- 好基友
line-height
和vertical-align
- 流的破坏
- 层叠规则
- 弹性布局
- 网格布局
- 文本控制
- 元素的显示与隐藏
基本概念
这些基本概念有些可能不易理解但却都很重要,如果看完还是很不理解的话需要自己谷歌或百度,网上关于这些概念的文章不少。
流
“流”又叫文档流,是 css 的一种基本定位和布局机制。流是 html 的一种抽象概念,暗喻这种排列布局方式好像水流一样自然自动。“流体布局”是 html 默认的布局机制,如你写的 html 不用 css,默认自上而下(块级元素如div
)从左到右(内联元素如span
)堆砌的布局方式。
块级元素和内联元素
这个大家肯定都知道。
块级元素是指单独撑满一行的元素,如 div、ul、li、table、p、h1
等元素。这些元素的 display 值默认是 block、table、list-item
等。
内联元素又叫行内元素,指只占据它对应标签的边框所包含的空间的元素,这些元素如果父元素宽度足够则并排在一行显示的,如 span、a、em、i、img、td
等。这些元素的 display 值默认是 inline、inline-block、inline-table、table-cell
等。
实际开发中,我们经常把 display
计算值为 inline
inline-block
inline-table
table-cell
的元素叫做内联元素,而把 display
计算值为 block
的元素叫做块级元素。
width: auto 和 height: auto
width
、height
的默认值都是auto
。
对于块级元素,width: auto
的自动撑满一行。
对于内联元素,width: auto
则呈现出包裹性,即由子元素的宽度决定。
无论内联元素还是块级元素,height: auto
都是呈现包裹性,即高度由子级元素撑开。但是父元素设置 height: auto
会导致子元素 height: 100%
百分比失效。
流体布局之下,块级元素的宽度 width: auto
是默认撑满父级元素的。这里的撑满并不同于 width: 100%
的固定宽度,而是像水一样能够根据 margin
不同而自适应的宽度。
css 的属性非常有意思,正常流下,如果块级元素的 width
是个固定值,margin
是 auto
,则margin
会撑满剩下的空间;如果 margin
是固定值,width
是 auto
,则width
会撑满剩下的空间。这就是流体布局的根本所在。
外在盒子和内在盒子
外在盒子是决定元素排列方式的盒子,即决定盒子具有块级特性还是内联特性的盒子。外在盒子负责结构布局。
内在盒子是决定元素内部一些属性是否生效的盒子。内在盒子负责内容显示。
如 display: inline-table;
外在盒子就是inline
,内在盒子就是table
。外在盒子决定了元素要像内联元素一样并排在一排显示,内在盒子则决定了元素可以设置宽高、垂直方向的 margin 等属性。如下图
右侧的 table 和左侧的文字在一行排列(外在盒子 inline 的表现特征),同时有拥有自定义宽度 111px(内在盒子 table 可以设置宽高)。
css 权重和超越!important
css 选择器权重列表如下:
权重 | 选择器 |
---|---|
10000 | !important |
1000 | 内联样式:style=”” |
100 | ID 选择器:#idName{...}
|
10 | 类、伪类、属性选择器:.className{...} / :hover{...} / [type="text"] ={...}
|
1 | 标签、伪元素选择器:div{...} / :after{...}
|
0 | 通用选择器(*)、子选择器(>)、相邻选择器(+)、同胞选择器(~) |
在 css 中,!important
的权重相当的高,但是由于宽高会被 max-width
/min-width
覆盖,所以 !important
会失效。
width: 100px!important;
min-width: 200px;
上面代码计算之后会被引擎解析成:
width: 200px;
盒模型(盒尺寸)
元素的内在盒子是由 margin box
、border box
、padding box
、content box
组成的,这四个盒子由外到内构成了盒模型。
IE 模型:box-sizing: border-box
此模式下,元素的宽度计算为 border+padding+content
的宽度总和。
w3c 标准模型):box-sizing: content-box
此模式下,元素的宽度计算为 content
的宽度。
由于 content-box
在计算宽度的时候不包含 border pading
很烦人,而且又是默认值,业内一般采用以下代码重置样式:
:root {box-sizing: border-box;}
* {box-sizing: inherit;}
内联盒模型
内联元素是指外在盒子默认是内联盒子的元素。从表现来说,内联元素的典型特征就是可以和文字在一行显示。因此文字也是内联元素。图片、按钮、输入框、下拉框等替换元素也是内联元素。内联盒模型是指内联元素包含的几个盒子,理解记忆下面的几个概念对 css 的深入学习极其重要。
- 内容区域:本质上是字符盒子。在浏览器中,文字选中状态的背景色就是内容区域。
- 内联盒子:内联盒子就是指元素的外在盒子是内联的,会和其他内联盒子排成一行。
- 行框盒子:由内联元素组成的每一行都是一个行框盒子。行框盒子由一个个内联盒子组成,如果换行,那就是两个行框盒子。比如一个不换行的的
p
标签,就存在一个行框盒子。值得注意的是,如果给元素设置display: inline-block
,则创建了一个独立的行框盒子。line-height 是作用在行框盒子上的,并最终决定高度。 - 包含盒子:就是包含块。多行文字组成一个包含块。
- 幽灵空白节点:内联元素的每个行框盒子前面有一个“空白节点”一样,这个“空白节点”不占据任何宽度,无法选中获取,但是又实实在在存在,表现就如同文本节点一样。
替换元素
替换元素是指内容可以替换的元素,实际上就是 content box
可以被替换的元素。如存在 src=""
属性的 <img> <audio> <video> <iframe>
元素和可以输入文本的 <input> <select> <textarea>
元素等。
所有替换元素都是内联元素,默认 display
属性是 inline
或inline-block
(除了 input[type="hidden"]
默认display: none;
)。
替换元素有自己默认的样式、尺寸(根据浏览器不同而不同),而且其 vertical-align
属性默认是bottom
(非替换元素默认值是baseline
)。
盒模型四大金刚
content
对于非替换元素如 div
, 其content
就是 div 内部的元素。
而对于替换元素,其 content
就是可替换部分的内容。
CSS 中的 content
属性主要用伪元素 :before/:after
中,除了做字体库和少写个 div,对于一般开发来说并无卵用。
padding
padding
是四大金刚中最稳定的了,少见有什么异常。尽管如此还是有些需要注意的地方:
- 大部分情况下我们会将元素重置为
box-sizing: border-box
,宽高的计算是包含了padding
的,给人一种padding
也是content box
一部分的感觉,好像line-height
属性也作用于padding
上。但实际上,元素真正的内容的宽高只是content box
的宽高,而line-height
属性是不作用于padding
的。
-
padding
不可为负值,但是可以为百分比值。为百分比时水平和垂直方向的padding
都是相对于元素宽度计算的。将一个div
设为padding: 100%
就能得到一个正方形,padding: 10% 50%
可以得到一个宽高比 5:1 的矩形。
body {width: 400px;}
.box {padding: 10% 50%;}
-
padding
配合background-clip
属性,可以制作一些特殊形状:
/* 三道杠 */
.icon1 {
display: inline-block;
width: 12px;
height: 10px;
padding: 2px 0;
border-top: 2px solid currentColor;
border-bottom: 2px solid currentColor;
background: currentColor; /* 注意如果此处背景颜色属性用缩写的话,需要放到其他背景属性的前面,否则会覆盖前面的属性值(此处为 background-clip)为默认值 */
background-clip: content-box;
}
/* 双层圆点 */
.icon2 {
display: inline-block;
width: 12px;
height: 10px;
padding: 2px;
border: 2px solid currentColor;
border-radius: 50%;
background-color: currentColor;
background-clip: content-box;
}
预览如下:(currentColor 是 css 中为数不多的变量,指当前文字的颜色值,非常好用)
margin
- 作为外边距,
margin
属性并不会参与盒子宽度的计算,但通过设置margin
为负值,却能改变元素水平方向的尺寸:
<div>asdf</div>
<style>
div {margin: 0 -100px;}
</style>
此时 div
元素的宽度是比父级元素的宽度大 200px
的。但是这种情况只会发生在元素是流布局的时候,即元素 width
是默认的 auto
并且可以撑满一行的时候。如果元素设定了宽度,或者元素设置了 float: left
/ position: absolute
这样的属性改变了流体布局,那么 margin
为负也无法改变元素的宽度了。
-
块级元素的垂直方向会发生
margin
合并,存在以下三种场景:- 相邻兄弟元素之间
margin
合并; - 父元素
margin-top
和子元素margin-top
,父元素margin-bottom
和子元素margin-bottom
; - 空块级元素自身的
margin-top
和margin-botom
合并,例子如下。
- 相邻兄弟元素之间
要阻止 margin
合并,可以:1. 给元素设置 bfc;2. 设置 border 或 padding 阻隔 margin;3. 用内联元素(如文字)阻隔;4. 给父元素设定高度。
-
margin
的百分比值跟padding
一样,垂直方向的margin
和水平方向上的一样都是相对于宽度计算的。
<div class="box">
<div></div>
</div>
<style>
.box{
overflow: hidden;
background-color: lightblue;
}
.box > div{margin: 50%;}
</style>
此时 .box 是一个宽高比 2:1 的矩形,因为空块级元素自身的垂直方向的 margin
发生了合并。
这里父元素设置 overflow: hidden
是利用 bfc
的特性阻止子元素的 margin
和父元素合并,换成其他 bfc
特性或者设置 1px
的 border
/ padding
都是可以达到效果的。
-
margin: auto
能在块级元素设定宽高之后自动填充剩余宽高。margin: auto
自动填充触发的前提条件是元素在对应的水平或垂直方向具有自动填充特性,显然默认情况下块级元素的高度是不具备这个条件的。典型应用是块级元素水平局中的实现:
display: block;
width: 200px;
margin: 0 auto;
auto
的特性是,如果两侧都是 auto
,则两侧均分剩余宽度;如果一侧margin
是固定的,另一侧是 auto
,则这一侧auto
为剩余宽度。栗子:
这个特性鲜为人知,且很实用。
除了水平方向,垂直方向的 margin
也能实现垂直居中,但是需要元素在垂直方向具有自动填充特性,而这个特性可以利用 position
实现:
position: absolute;
left: 0; right: 0; top: 0; bottom: 0;
width: 200px;
height: 200px;
margin: auto;
border
border
主要作用是做边框。border-style
属性的值有 none/solid/dashed/dotted/double
等,基本看名字就能猜出什么来了:
border-width
属性的默认值是 3px
,是为了照顾小弟border-style: double
,你懂的。值得注意的是,border-color
默认是跟随字体的颜色,相当于默认设置了 border-color: currentColor
一样。
border
另一广受欢迎的功能就是图形构建,特别是做应用广泛的三角形,其原理可看下图的 1 -3:
div{
float: left;
margin: 20px;
}
div:nth-child(1){
width: 20px;
height: 20px;
border: 20px solid;
border-color: blue red orange green;
}
div:nth-child(2){
width: 20px;
height: 20px;
border: 20px solid;
border-color: blue transparent transparent transparent;
}
div:nth-child(3){
border: 20px solid;
border-color: blue transparent transparent transparent;
}
div:nth-child(4){
border-style: solid;
border-width: 40px 20px;
border-color: blue transparent transparent transparent;
}
div:nth-child(5){
border-style: solid;
border-width: 40px 20px;
border-color: blue red transparent transparent;
}
其实就是将其他三个边框的颜色设置透明,并把宽高设为 0。图中 4 - 5 两个图形,是通过调整边框宽度和颜色调整三角形的形状,把最后一个图的红色改为蓝色,则是一个直角三角形了。
好基友 line-height
和vertical-align
line-height
和 vertical-align
是控制元素垂直对齐的两大属性,也是最难理解搞懂的属性。
字母 x 的角色
在内联元素的垂直方向对齐中,基线是最为重要的概念。line-height
定义的就是两基线之间的距离,vertical-align
的默认值就是基线。基线的定义则是字母 x 的下边缘。
css 中有个概念叫 x-height
,指的是小写字母 x 的高度。vertical-align: middle
对齐的就是基线往上 1 /2x-height
高度的地方,可以理解为近似字母 x 的交叉点。
css 中除了 px/em/rem
等,还有个单位是ex
。指的就是小写字母 x 的高度,即x-height
。用处不大,不再介绍。
line-height
-
line-height
各类属性值
normal
:默认值 normal
其实是类型为数值的变量,根据浏览器和字体 ’font-family’ 不同而不同,一般约为 1.2。
数值和百分比:最终会被计算为带单位的值,具体计算方法就是乘以字体大小font-size
。
长度值:就是 100px
这样带单位的值。
这几类值的继承特性不同:line-height
是数值的元素的子元素继承的就是这个数值,百分比 / 长度值继承的都是计算后得出的带单位的值(px)。
-
line-height
的作用:
line-height
属性用于设置多行元素的空间量,如多行文本的间距。
对块级元素来说,line-height
决定了 行框盒子 的最小高度。注意是行框盒子的最小高度,而不是块级元素的实际高度。(图中两个 div
行高一样,div.one
的背景色区域就是行框盒子的高度,而 div.two
的背景区域则是实际高度,其行框盒子高度和 div.one
是一样的。)
对于非替代的 inline 元素,它用于计算行框盒子的高度。此时内联元素的行框盒子的高度完全由 line-height
决定,不受其他任何属性的影响。
-
line-height
实现垂直居中的本质:行距
行距是指一行文本和相邻文本之间的距离。行距 = line-height
— font-size
。行距具有上下等分的机制:意思就是文字上下的行距是一样的,各占一半,这也是 line-height
能让内联元素垂直居中的原因。下图中字母 x 上下行距各占一半,共同撑起了div
。
下图中和上图唯一不同之处就是多了个 display: inline-block
的span
元素,但是此处的 span
元素并没有影响 div
元素的高度,而只是靠着 vertical-align: middle
属性将自身中心点对齐了字母 x 的交叉点实现垂直居中而已。div
元素的高度仍然和上图一模一样,由字母 x 和行距共同撑起。此时如果删除字母 x,div
的高度不变,因为 span
元素的行框盒子前会产生幽灵空白节点,而幽灵空白节点 + 行高也能撑起div
。
- 内联元素的大值特性
<div class="box">
<span>asdf</span>
</div>
样式 1:此时 .box 高度是多少?
.box {
line-height: 100px;
background: lightgreen;
}
.box span {line-height: 30px;}
样式 2:此时 .box 高度是多少?
.box {
line-height: 30px;
background: lightgreen;
}
.box span {line-height: 100px;}
先说结论:无论内联元素的 line-height
如何设置,最终父元素的高度都是数值大的那个 line-height
决定的。
样式 1 中,span
元素的行框盒子前存在一个幽灵空白节点,而这个幽灵空白节点的行高是 100px;样式 2 中,幽灵空白节点的行高是 30px,但是这时 span 元素的行高是 100px。两种情况其实一样,取大值而已。
vertical-align
-
vertical-align
的属性值
线类:如 baseline(默认值)
top
middle
bottom
(baseline
使元素的基线与父元素的基线对齐,middle
使元素的中部与父元素的基线往上 x-height
的一半对齐。top
bottom
使元素及其后代元素的底部与整行或整块的底部对齐。)
文本类:text-top
text-bottom
(使元素的顶部与父元素的字体顶部对齐。)
上标下标:sub
super
(使元素的基线与父元素的下标基线对齐。)
数值:20px
2em
(默认值 baseline
相当于数值的 0。使元素的基线对齐到父元素的基线之上的给定长度,数值正值是基线往上偏移,负值是往下偏移,借此可以实现元素垂直方向精确对齐。)
百分比:20%
(使元素的基线对齐到父元素的基线之上的给定百分比,该百分比是 line-height 属性的百分比。)
-
vertical-align
的作用前提
vertical-align 属性起作用的前提必须是作用在内联元素上。 即 display
计算值为 inline
inline-block
inline-table
table-cell
的元素。所以如果元素设置了 float: left
或者 position: absolute
,则其vertical-align
属性不能生效,因为此时元素的 display
计算值为 block
了。
- 好基友
line-height
、vertical-align
和第三者幽灵空白节点的爱恨情仇
有时候会遇见下面这样高度和设置不一致的情况:
div
的实际高度比设定的行高大了,为什么呢?
内联元素的默认对齐方式是 baseline
,所以此时此时span
元素的基线是和父元素的基线相对齐的,而此时父元素的基线在拿呢?
父元素的基线其实就是行框盒子前的幽灵空白节点的基线。把幽灵空白节点具象化为字母 x
可能容易理解些:
由于 div
行高是 30px
,所以字母x
和span
元素的高度都是 30px
。但是字母 x 的font-size
较小,span
元素的 font-size
较大,而行高一样的情况下 font-size
越大基线的位置越偏下,所以两者的基线不在同一水平线上。如下图左边部分:
由于内联元素默认基线对齐,所以字母 x
和span
元素发生了位移以使基线对齐,导致 div
高度变大。而此时字母 x
的半行距比 span
元素的半行距大,大出的部分就是 div
的高度增加的部分。
-
display: inline-block
基线的不同之处
先看例子,图中 span
元素设置了 display: inline-block
和宽高,从而撑起了父元素 div
的高度,但 span
本身并无 margin
属性,那为什么底部和 div
下边缘之间会有空隙呢?
这就要说到 inline-block
的不同之处了。一个设置了 display: inline-block
的元素:
- 如果元素内部没有内联元素,则该元素基线就是该元素下边缘;
- 如果元素设置了
overflow
为hidden auto scroll
,则其基线就是该元素下边缘; - 如果元素内部还有内联元素,则其基线就是内部最后一行内联元素的基线。
知道了这点,那么再回到上面的例子:
原来是第三者幽灵空白节点搞的鬼。此时 span
的行框盒子前,还存在一个幽灵空白节点。由于 span
元素默认基线对齐,所以 span
元素的基线也就是其下边缘是和幽灵空白节点的基线对齐的。从而导致幽灵空白节点基线下面的半行距撑高了 div
元素,造成空隙。如下图:
如果 span
元素中存在内联元素呢?
可以看到,此时 span
元素下边缘的空隙没了,因为此时 span
元素的基线是内部最后一行内联元素的基线。
值得一提的是,由于替换元素内部不可能再有别的元素,所以其基线位置永远位于下边缘。
- 解决问题
间隙产生本质上是由基线对齐引发的错位造成的,源头上是 vertical-align
和line-height
共同造成的,所以要想解决这个问题,只要直接或间接改造两个属性中的一个就行了:
- 给元素设置块状化
display: block
使vertical-align
属性失效; - 尝试不同的
vertical-align
值如bottom/middle/top
; - 直接修改
line-height
值; - 如果
line-height
为相对值如1.4
,设置font-size: 0
间接改变line-height
。
- 弹框 dialog
下面是张鑫旭大佬推荐的利用 vertical-align
实现的水平垂直居中弹框,能够理解的话就说明你已经完全掌握了好基友和第三者的关系了。
<div class="container">
<div class="dialog"> 自适应弹出层 </div>
</div>
<style>
.container{
position: fixed;
top: 0; right: 0; bottom: 0; left: 0;
background-color: rgba(0, 0, 0, .15);
text-align: center;
font-size: 0;
white-space: nowrap;
overflow: auto;
}
.container:after{
content: '';
display: inline-block;
height: 100%;
vertical-align: middle;
}
.dialog{
display: inline-block;
width: 400px;
height: 400px;
vertical-align: middle;
text-align: left;
font-size: 14px;
white-space: normal;
background: white;
}
</style>
流的破坏
现在 UI 框架横行的年代,我们的 css 写的越来越少了。这对于很多老鸟来说是件好事,但是对于初入前端的小白却未必。因为写的少了,就少了很多练手和总结的机会,对于很多样式理解就不透彻。本章介绍的 float
、position
和BFC
对于前端页面布局非常重要,希望诸位看官们静下心来仔细研读。
float
属性的特性
float
属性应该是 css 世界最令人意外的属性了,倒不是因为他的表现,而是他的设计初衷竟然只是为了实现“文字环绕图片”的效果。只不过因为 float
属性的一些特性,才导致其被到处使用以致于产生了诸多不利于维护的页面。
下面看看 float
属性的特性:
- 包裹性:即此时元素
width
会像height
一样由子元素决定,而不是默认撑满父元素。 - 块状化并格式化上下文:这个就是后面会讲的 BFC 特性。块状是指元素设置
float: left
之后,其display
的计算值就成了block
。格式化上下文是指会创建一个 BFC,这个后面会讲。 - 没有任何
margin
合并; - 脱离文档流:
float
设计的初衷就是为了“文字环绕”效果,为了让文字环绕图片,就需要具备两个条件。第一是元素高度坍塌,第二是行框盒子不可与浮动元素重叠。而元素高度坍塌就导致元素后面的非浮动块状元素会和其重叠,于是他就像脱离文档流了。
前三个特性都是正能量满满,但是最后一个特性却给我们开发者带来了很多麻烦,需要用到 clear
来清除浮动。
clear
的作用和不足
大家都知道 clear: both
可以清除前面浮动元素的浮动,但实际上,他并不是真的清除了浮动。
clear
的定义是:元素盒子的边不能与前面的浮动元素相邻。也就是虽然浮动元素高度坍塌,但是设置了 clear: both
的元素却将其高度视为仍然占据位置。
clear
只能作用于块级元素,并且其并不能解决后面元素可能发生的文字环绕问题。
BFC:块级格式化上下文
- 概念
BFC 是一个独立的渲染区域,只有 Block-level box
参与,它规定了内部的 Block-level Box
如何布局,并且与这个区域外部毫不相干。
BFC 就好像一个结界,结界里面的东西不能影响外面的布局,也就是说,BFC 的子元素再翻江倒海,都不会影响外面的元素。 所以:
- BFC 本身不会发生
margin
重叠。 - BFC 可以彻底解决子元素浮动带来的的高度坍塌和文字环绕问题。
- BFC 的创建方法
- 根元素 <html>;
- 浮动元素 (
float
不为none
的元素); - 绝对定位元素 (元素的
position
为absolute
或fixed
); -
inline-blocks
(元素的display: inline-block
); - 表格单元格 (元素的
display: table-cell
,HTML
表格单元格默认属性); -
overflow
的值不为visible
的元素; - 弹性盒
flex boxes
(元素的display: flex
或inline-flex
);
BFC 包含创建该上下文元素的所有子元素,但不包括创建了新 BFC 的子元素的内部元素。
- 特性
- 内部的盒会在垂直方向一个接一个排列(可以看作 BFC 中有一个的常规流);
- Box 垂直方向的距离由
margin
决定。属于同一个 BFC 的两个相邻 Box 的margin
会发生重叠; - 每一个盒子的左外边距应该和包含块的左边缘相接触。即使存在浮动也是如此,除非子盒子形成了一个新的 BFC。
- BFC 就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之亦然;
- 计算 BFC 的高度时,考虑 BFC 所包含的所有元素,连浮动元素也参与计算;
- BFC 的区域不会与
float box
重叠;
乍一看还挺多的,但真正要注意并用心理解的只有 3 4 6。
特性 1 中内部的盒是指块级盒。因为 <html>
根元素也是 BFC,所以我们平常写的 div p
都是独自占一行。
特性 2 <html>
是 BFC,所以里面的元素垂直方向的 margin
会发生折叠。但是,直接子孙元素与该 BFC 上下边界 margin
不能折叠,保证了 BFC 内部的元素不会影响外部的元素。两个上下相邻的 BFC 之间折不折叠要看具体情况,如 display: inline-block
float: left
不会折叠,而 overflow: hidden
则会折叠。
特性 3 完全解读:
In a block formatting context, each box's left outer edge touches the left edge of the containing block (for right-to-left formatting, right edges touch). This is true even in the presence of floats (although a box's line boxes may shrink due to the floats), unless the box establishes a new block formatting context (in which case the box itself may become narrower due to the floats).
网上很多翻译成“每个元素的 margin box
的左边,与包含块 border box
的左边相接触”的,这样的翻译是不准确甚至错误的,曾给我造成莫大的迷茫。正确的翻译是“每一个盒子的左外边距应该和包含块的左边缘相接触”。
第一,包含块未必就是父级元素。对于 position: absolute
来说,包含块是指第一个 positoin
不为 static
的祖先元素。
第二,BFC 中的盒子应该与其自身的包含块相接触,而非与 BFC 盒子本身相接触。
第三,BFC 中的盒子是与其包含块的 left edge
相接触,而不是包含块的 left-border
相接触。left edge
正确的翻译为左边缘。左边缘可能是 content box
的左边缘(非绝对定位如 position: absolute
),也可能是padding box
的左边缘(如position: absolute
)。
理解了上面两点,其实特性 3 就是普通的流布局和定位布局默认贴着“左侧”思想的总结。
如图,aside
元素的 margin box
的左边距和 BFC 元素的左边缘相接触。并且由于 float box
高度坍塌,main
占据了 body
全部空间并且和 BFC 盒子左边缘相接触(特性 3“即使存在浮动也是如此”)。
特性 4 正是 BFC 存在的意义。它规定了 BFC 子元素无论margin-top: -10000px
float: left
等都不会影响到 BFC 外部的元素的布局。所以 BFC 是最好的清除浮动的方式,连浮动的文字环绕问题都能解决。
特性 5 BFC 计算高度时包含浮动元素的高度。可以利用 BFC 此特性解决浮动元素高度坍塌的问题。
特性 6:利用特性 6 实现自适应两栏布局。此时 main
宽度是自适应的。
绝对定位position: absolute
- 定义
和浮动元素一样,绝对定位也具有块状化、BFC、包裹性、脱离文档流、没有 margin
合并的特性。
但和浮动不同的是,绝对定位是完全的脱离文档流。大家还记得浮动产生的目的就是为了实现文字环绕效果,所以浮动元素虽然脱离了文档流,但是后面的文字还是会环绕在浮动元素周围。而绝对定位一但产生,就不会再对周围元素产生任何影响。
而且两者包含块不同,浮动元素包含块只能是父级元素,绝对定位的包含块则是距离最近的 position
不为 static
的祖先元素。
- 无依赖绝对定位
大多数用到绝对定位的时候,都是存在包含块和 left/top
等方向属性的。但其实 position: absolute
是非常独立的 css 属性,其样式和行为表现不依赖任何 css 属性就可以完成。
可以看出,无依赖的 position: absolute
元素定位的位置和其本身无定位属性时候的位置和 display
的值有关。如果元素在没有 position
的情况下是内联元素,则和内联元素在同一行显示;如果元素在没有 position
属性的情况下是块级元素,则换行显示。
无依赖绝对定位的实用性虽然还行,但是其功能却完全可以用 left/top
实现。所以了解即可,如果有兴趣可以自行尝试。
- 绝对定位和
overflow: hidden
其实一句话就可以表示两者之间的关系:当 overflow: hidden
元素在绝对定位元素和其包含块之间的时候,绝对定位元素不会被剪裁。
以下两种绝对定位元素不会被剪裁:
<div style="overflow: hidden;">
<img src="big.jpg" style="position: absolute;">
</div>
<div style="position: relative;">
<div style="overflow: hidden;">
<img src="big.jpg" style="position: absolute;">
</div>
</div>
以下两种绝对定位元素会被剪裁:
<div style="overflow: hidden; position: relative;">
<img src="big.jpg" style="position: absolute;">
</div>
<div style="overflow: hidden;">
<div style="position: relative;">
<img src="big.jpg" style="position: absolute;">
</div>
</div>
-
position: absolute
的流体特性
当绝对定位元素的水平方向 (left/right
) 或垂直方向 (top/bottom
) 的两个定位属性同时存在的时候,绝对元素在该方向上便具有了流体特性。此时的 width/height
属性具有自动撑满的特性,和一个正常流的 div
元素的 width
属性别无二致。如图,设置了固定 margin
值的元素,宽高 auto
能够自动适应剩余空间:
同样的,设置了固定宽高的元素,如果 margin: auto
,则margin
平分剩余空间导致垂直水平居中:
层叠规则
层叠规则是指当网页中的元素发生层叠时侯的遵循的规则。
层叠上下文
层叠上下文好像是一个结界,层叠上下文内的元素如果跟层叠上下文外的元素发生层叠,则比较该层叠上下文和外部元素的层叠上下文的层叠水平高低。
创建一个层叠上下文的方法就是给 position
值为 relative/aboslute/fixed
的元素设置 z-index
不为 auto
的值。
层叠上下文内元素的层叠水平如下图:
- 最底层的
border/background
是指当前层叠上下文元素的边框和背景色。z-index
为负值的元素在其之上。
如下图所示 .dad
元素默认设置 z-index: auto
,没有创建层叠上下文,此时其就是一个普通的块级盒子,所以设置了z-index: -1
的.son
元素跑到了爸爸身后看不见了。
而由于 .mom
设置了 z-index: 0
,创建出了一个层叠上下文,所以.son
元素就算设置了 z-index: -1
也跑不出老妈的视线。地址
- 当块级元素和内联元素发生层叠,内联元素居于块级元素之上。如下图:地址
- 普通定位元素层叠水平在普通元素之上。普通定位元素是指
z-index
为auto
的定位元素。下图span
就是普通定位元素:地址
CSS3 新增层叠上下文
CSS3 带来了很多新属性,其中很不惹人注意的一点就是增加了很多会自动创建层叠上下文的属性:
- 元素的
opacity
值不为1
,也就是透明元素; - 元素的
transform
值不为none
; - 元素的
filter
值不为none
; - 元素的设置
-webkit-overflow-scrolling: touch
; -
z-index
不为auto
的弹性盒子的子元素; - 元素的
isolation
值为isolate
; - 元素的
mix-blend-mode
值不为normal
; - 元素的
will-change
值为opacity/transform/filter/isolation/mix-blend-mode
中的一个。
这些属性大都不支持z-index
,所以他们都默认z-index: auto
,跟普通定位元素层叠水平一样,所以如果发生层叠会后来居上:地址
但是弹性盒子 display: flex
不同,弹性盒子的子元素支持设置 z-index
,且设置了数值的z-index
也会自动创建层叠上下文。如下图,可以看到设置了 z-index: 0
的元素层叠水平更高。地址
弹性布局
弹性布局是指 display: flex
或display: inline-flex
的布局。注意,设为弹性布局以后,子元素的 float、clear、vertical-align
属性都会失效。参见阮一峰大佬的 Flex 布局教程。
主要属性应用如下:
网格布局
网格布局(Grid)是最强大的 CSS 布局方案。注意,设为网格布局以后,容器子元素(项目)的 float、display: inline-block、display: table-cell、vertical-align 和 column-* 等
设置都将失效。参见阮一峰大佬的 CSS Grid 网格布局教程。
文本控制
以下 css 属性为文本相关。
::first-letter
应用实例
::first-letter
选中首个字符:地址
text-transform
应用
假设有个输入框只能输入大写字母,那么如下设置,输入小写字母出现的却是大写字母,可用于身份证输入框或验证码输入框等:
input {text-transform: uppercase;}
word-spacing
空格间隙
不要被表面意思误导,word-spacing
指的是字符“空格”的间隙。如果一段文字中没有空格,则该属性无效。下面代码设定空格间隙是 20px
,也就是说空格现在占据的宽度是原有的空格宽度 +20px
的宽度:
<p> 我有空 格,我该死......</p>
<style>
p {word-spacing: 20px;}
</style>
white-space
空白处理
我们都知道如果在 html
中输入多个空白符,默认会被当成一个空白符处理,实际上就是这个属性控制的:地址
- normal:合并空白符和换行符;
- nowrap:合并空白符,但不许换行;
- pre:不合并空白符,并且只在有换行符的地方换行;
- pre-wrap:不合并空白符,允许换行符换行和文本自动换行;
text-align: justify
text-align: justify
为两端对齐。除了实现文字的两端对齐,还能用来做一些两端对齐的布局。(注意下面例子自己测试时需要保证每行三个方块!!!)下面介绍个两端对齐布局的实例:地址
由于 text-align: justify
最后一行是左对齐,所以利用了三个空的 i
标签模拟最后一行。虽然实现了两端对齐,但是最后一行却出现间隙。根据之前的经验应该是 vertical-align
和line-height
搞的鬼,我们给 i
标签加上 outline
并用字母 x 模拟幽灵空白节点,现形:地址
上图分析:首先第一个 i
标签基线与第二行的 span
标签中的数字的基线对其,所以其位置在中间。其次最后一行的 i
标签基线对齐幽灵空白节点字母 x 的基线,没有错位,所以此时最后一行的间隙高度就是字母 x 的高度。所以很容易想到把幽灵空白节点的行高设为 0 来解决问题:地址
然而间隙虽然缩小了,但是还是存在。此时由于行高为 0,幽灵空白节点也就是字母 x 在页面中占用的真实位置其实是红线所示。也就是说虽然字母 x 还显示在页面上,但是其真实高度已经为 0,此时其中线、上边缘线、下边缘线合一,都在红线位置,其真实位置自然也就在红线位置。然而其基线却不会改变,在字母 x 下边缘。
此时 i
标签的基线发生错位,位移到下面与幽灵空白节点基线对齐,导致产生了间隙。
所以只需要再改变 i
标签的对齐方式,就能彻底清除间隙:地址
此时 i
标签的基线对齐其幽灵空白节点的下边缘线,没有了错位,也就没有了间隙。
如果改为 vertical-align: top
是一样的,因为合一了。但是 vertical-align: middle
却不行,因为此 middle
的位置是基线往上 1/2 个 e-height
的地方。
好吧本例结束了,没想到解释起来这么复杂。好好理解此例加深对 vertical-align
和line-height
的理解。
元素的显示与隐藏
此章节未完待续 ……