前端基础篇之CSS世界

 
 

作者:幻灵尔依
原文链接:https://juejin.im/post/5ce607a7e51d454f6f16eb3d

我想你每天写css代码有时候也会觉得很痛苦:这个布局的css怎么这么难实现!我也经常会有这种感觉,一个看似简单的布局总是要琢磨半天才能实现,偶尔还会出现一些怪异的超出理解的现象。这是因为我们对css只是大概知道个形,并没有看透css的本质。在同事的推荐下我阅读了张鑫旭老师的《css世界》,才发现css跟想象中的不太一样。本文为《css世界》个人总结笔记,为缩减篇幅丢弃了张老师冗余的小幽默,丢掉了些含金量较低的章节内容,因为ie已经被淘汰出局,所以有关css兼容性的地方也全部忽略不记,同时对个人觉得不易理解的地方加上了一些自己的理解和验证,所以错误之处还望指正。

基本概念

这些基本概念有些可能不易理解但却都很重要,如果看完还是很不理解的话需要自己谷歌或百度,网上关于这些概念的文章不少。

“流”又叫文档流,是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

widthheight的默认值都是auto

对于块级元素,width: auto的自动撑满一行。

对于内联元素,width: auto则呈现出包裹性,即由子元素的宽度决定。

无论内联元素还是块级元素,height: auto都是呈现包裹性,即高度由子级元素撑开。但是父元素设置height: auto会导致子元素height: 100%百分比失效。

流体布局之下,块级元素的宽度width: auto是默认撑满父级元素的。这里的撑满并不同于width: 100%的固定宽度,而是像水一样能够根据margin不同而自适应的宽度。

css的属性非常有意思,正常流下,如果块级元素的width是个固定值,marginauto,则margin会撑满剩下的空间;如果margin是固定值,widthauto,则width会撑满剩下的空间。这就是流体布局的根本所在。

外在盒子和内在盒子

外在盒子是决定元素排列方式的盒子,即决定盒子具有块级特性还是内联特性的盒子。外在盒子负责结构布局。

内在盒子是决定元素内部一些属性是否生效的盒子。内在盒子负责内容显示。

display: inline-table; 外在盒子就是inline,内在盒子就是table。外在盒子决定了元素要像内联元素一样并排在一排显示,内在盒子则决定了元素可以设置宽高、垂直方向的margin等属性。如下图

640?wx_fmt=png

右侧的table和左侧的文字在一行排列(外在盒子inline的表现特征),同时有拥有自定义宽度111px(内在盒子table可以设置宽高)。

css权重和超越 !important

曾经有道面试题把我难住了:

// 假设下面样式都作用于同一个节点元素`span`,判断下面哪个样式会生效body#god div.dad span.son {width: 200px;}body#god span#test {width: 250px;}
body#god div.dad span.son {width200px;}
body#god span#test {width250px;}

可怜当时做了三年前端的我竟然还不知道css有权重?

css选择器权重列表如下:

640?wx_fmt=png

在css中,!important的权重相当的高,但是由于宽高会被max-width/min-width覆盖,所以!important会失效。

width: 100px!important;min-width: 200px;px!important;
min-width: 200px;

上面代码计算之后会被引擎解析成:

width: 200px;px;

盒模型(盒尺寸)

元素的内在盒子是由margin boxborder boxpadding boxcontent 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;}
  box-sizing: border-box;    
}
* {
  box-sizing: inherit;
}

内联盒模型

内联元素是指外在盒子默认是内联盒子的元素。从表现来说,内联元素的典型特征就是可以和文字在一行显示。因此文字也是内联元素。图片、按钮、输入框、下拉框等替换元素也是内联元素。内联盒模型是指内联元素包含的几个盒子,理解记忆下面的几个概念对css的深入学习极其重要。

  1. 内容区域:本质上是字符盒子。在浏览器中,文字选中状态的背景色就是内容区域。

  2. 内联盒子:内联盒子就是指元素的外在盒子是内联的,会和其他内联盒子排成一行。

  3. 行框盒子:由内联元素组成的每一行都是一个行框盒子。行框盒子由一个个内联盒子组成,如果换行,那就是两个行框盒子。比如一个不换行的的p标签,就存在一个行框盒子。值得注意的是,如果给元素设置display: inline-block,则创建了一个独立的行框盒子。line-height是作用在行框盒子上的,并最终决定高度。

  4. 包含盒子:就是包含块。多行文字组成一个包含块。

  5. 幽灵空白节点:内联元素的每个行框盒子前面有一个“空白节点”一样,这个“空白节点”不占据任何宽度,无法选中获取,但是又实实在在存在,表现就如同文本节点一样。

替换元素

替换元素是指内容可以替换的元素,实际上就是content box可以被替换的元素。如存在src=""属性的<img> <audio> <video> <iframe>元素和可以输入文本的<input> <select> <textarea>元素等。

所有替换元素都是内联元素,默认display属性是inlineinline-block(除了input[type="hidden"]默认display: none;)。

替换元素有自己默认的样式、尺寸(根据浏览器不同而不同),而且其vertical-align属性默认是bottom(非替换元素默认值是baseline)。


盒模型四大金刚

content

对于非替换元素如div,其content就是div内部的元素。
而对于替换元素,其content就是可替换部分的内容。

CSS中的content属性主要用伪元素:before/:after中,除了做字体库和少写个div,对于一般开发来说并无卵用。

padding

padding是四大金刚中最稳定的了,少见有什么异常。尽管如此还是有些需要注意的地方:

  1. 大部分情况下我们会将元素重置为box-sizing: border-box,宽高的计算是包含了padding的,给人一种padding也是content box一部分的感觉,好像line-height属性也作用于padding上。但实际上,元素真正的内容的宽高只是content box的宽高,而line-height属性是不作用于padding的。

640?wx_fmt=png
  1. padding不可为负值,但是可以为百分比值。为百分比时水平和垂直方向的padding都是相对于父级元素宽度计算的。将一个div设为padding: 100%就能得到一个正方形,padding: 10% 50%可以得到一个宽高比 5:1 的矩形。

body {  width: 400px;}.box {  padding: 10% 50%;}
  width400px;
}
.box {
  padding10% 50%;
}
640?wx_fmt=png
  1. padding配合background-clip属性,可以制作一些特殊形状:

/*三道杠*/.icon1 {  box-sizing: border-box;  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: 12px;  padding: 2px;  border: 2px solid currentColor;  border-radius: 50%;  background-color: currentColor;  background-clip: content-box;}
.icon1 {
  box-sizing: border-box;
  display: inline-block;
  width12px;
  height10px;
  padding2px 0;
  border-top2px solid currentColor;
  border-bottom2px solid currentColor;
  background: currentColor; /*注意如果此处背景颜色属性用缩写的话,需要放到其他背景属性的前面,否则会覆盖前面的属性值(此处为background-clip)为默认值*/
  background-clip: content-box;
}
/*双层圆点*/
.icon2 {
  display: inline-block;
  width12px;
  height12px;
  padding2px;
  border2px solid currentColor;
  border-radius50%;
  background-color: currentColor;
  background-clip: content-box;
}

预览如下:(currentColor是css中为数不多的变量,指当前文字的颜色值,非常好用)

640?wx_fmt=png

margin

  1. 作为外边距,margin属性并不会参与盒子宽度的计算,但通过设置margin为负值,却能改变元素水平方向的尺寸:

<div>asdf</div><style>  div {    margin: 0 -100px;  }</style></div>
<style>
  div {
    margin0 -100px;
  }
</style>

此时div元素的宽度是比父级元素的宽度大200px的。但是这种情况只会发生在元素是流布局的时候,即元素width是默认的auto并且可以撑满一行的时候。如果元素设定了宽度,或者元素设置了float: left / position: absolute这样的属性改变了流体布局,那么margin为负也无法改变元素的宽度了。

  1. 块级元素的垂直方向会发生margin合并,存在以下三种场景:

要阻止margin合并,可以:1. 给元素设置 bfc;2. 设置border或padding阻隔margin;3.  用内联元素(如文字)阻隔;4. 给父元素设定高度。

  1. margin的百分比值跟padding一样,垂直方向的margin和水平方向上的一样都是相对于父元素宽度计算的。

<div class="box">  <div></div></div><style>  .box{    overflow: hidden;    background-color: lightblue;  }  .box > div{    margin: 50%;  }</style>
  <div></div>
</div>
<style>
  .box{
    overflow: hidden;
    background-color: lightblue;
  }
  .box > div{
    margin50%;
  }
</style>

此时 .box 是一个宽高比 2:1 的矩形,因为空块级元素自身的垂直方向的margin发生了合并。

这里父元素设置overflow: hidden是利用 bfc 的特性阻止子元素的margin和父元素合并,换成其他 bfc 特性或者设置 1pxborder / padding都是可以达到效果的。

  1. margin: auto能在块级元素设定宽高之后自动填充剩余宽高。margin: auto自动填充触发的前提条件是元素在对应的水平或垂直方向具有自动填充特性,显然默认情况下块级元素的高度是不具备这个条件的。典型应用是块级元素水平局中的实现:

display: block;width: 200px;margin: 0 auto;block;
width: 200px;
margin: 0 auto;

auto的特性是,如果两侧都是auto,则两侧均分剩余宽度;如果一侧margin是固定的,另一侧是auto,则这一侧auto为剩余宽度。栗子:

640?wx_fmt=png

这个特性鲜为人知,且很实用。

除了水平方向,垂直方向的margin也能实现垂直居中,但是需要元素在垂直方向具有自动填充特性,而这个特性可以利用position实现:

position: absolute;left: 0; right: 0; top: 0; bottom: 0;width: 200px;height: 200px;margin: auto;absolute;
left: 0; right: 0; top: 0; bottom: 0;
width: 200px;
height: 200px;
marginauto;

border

border主要作用是做边框。border-style属性的值有none/solid/dashed/dotted/double等,基本看名字就能猜出什么来了:

640?wx_fmt=png

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;}
  float: left;
  margin20px;
}
div:nth-child(1){
  width20px;
  height20px;
  border20px solid;
  border-color: blue red orange green;
}
div:nth-child(2){
  width20px;
  height20px;
  border20px solid;
  border-color: blue transparent transparent transparent;
}
div:nth-child(3){
  border20px solid;
  border-color: blue transparent transparent transparent;
}
div:nth-child(4){
  border-style: solid;
  border-width40px 20px;
  border-color: blue transparent transparent transparent;
}
div:nth-child(5){
  border-style: solid;
  border-width40px 20px;
  border-color: blue red transparent transparent;
}
640?wx_fmt=png

其实就是将其他三个边框的颜色设置透明,并把宽高设为 0 。图中4-5两个图形,是通过调整边框宽度和颜色调整三角形的形状,把最后一个图的红色改为蓝色,则是一个直角三角形了。


好基友line-height和vertical-align

line-heightvertical-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

normal:默认值normal其实是类型为数值的变量,根据浏览器和字体'font-family'不同而不同,一般约为 1.2 。

数值和百分比:最终会被计算为带单位的值,具体计算方法就是乘以字体大小font-size

长度值:就是100px这样带单位的值。

这几类值的继承特性不同:line-height是数值的元素的子元素继承的就是这个数值,百分比/长度值继承的都是计算后得出的带单位的值(px)。

line-height属性用于设置多行元素的空间量,如多行文本的间距。

对块级元素来说,line-height决定了行框盒子的最小高度。注意是行框盒子的最小高度,而不是块级元素的实际高度。(图中两个div行高一样,div.one 的背景色区域就是行框盒子的高度,而 div.two 的背景区域则是实际高度,其行框盒子高度和 div.one 是一样的。)

640?wx_fmt=png

对于非替代的 inline 元素,它用于计算行框盒子的高度。此时内联元素的行框盒子的高度完全由line-height决定,不受其他任何属性的影响。

640?wx_fmt=png

行距是指一行文本和相邻文本之间的距离。行距 = line-heightfont-size。行距具有上下等分的机制:意思就是文字上下的行距是一样的,各占一半,这也是line-height能让内联元素垂直居中的原因。下图中字母x上下行距各占一半,共同撑起了div

640?wx_fmt=png

下图中和上图唯一不同之处就是多了个display: inline-blockspan元素,但是此处的span元素并没有影响div元素的高度,而只是靠着vertical-align: middle属性将自身中心点对齐了字母x的交叉点实现垂直居中而已。div元素的高度仍然和上图一模一样,由字母x和行距共同撑起。此时如果删除字母x,div的高度不变,因为span元素的行框盒子前会产生幽灵空白节点,而幽灵空白节点+行高也能撑起div

640?wx_fmt=png
<div class="box">  <span>asdf</span></div>
  <span>asdf</span>
</div>

样式1:此时 .box 高度是多少?

.box {  line-height: 100px;  background: lightgreen;}.box span {  line-height: 30px;}
  line-height100px;
  background: lightgreen;
}
.box span {
  line-height30px;
}

样式2:此时 .box 高度是多少?

.box {  line-height: 30px;  background: lightgreen;}.box span {  line-height: 100px;}
  line-height30px;
  background: lightgreen;
}
.box span {
  line-height100px;
}

先说结论:无论内联元素的line-height如何设置,最终父元素的高度都是数值大的那个line-height决定的。

样式1中,span元素的行框盒子前存在一个幽灵空白节点,而这个幽灵空白节点的行高是100px;样式2中,幽灵空白节点的行高是30px,但是这时span元素的行高是100px。两种情况其实一样,取大值而已。

vertical-align

线类:如baseline(默认值) top middle bottombaseline使元素的基线与父元素的基线对齐,middle使元素的中部与父元素的基线往上x-height的一半对齐。top bottom使元素及其后代元素的底部与整行或整块的底部对齐。)

文本类:text-top text-bottom(使元素的顶部与父元素的字体顶部对齐。)

上标下标:sub super(使元素的基线与父元素的下标基线对齐。)

数值:20px 2em (默认值baseline相当于数值的 0 。使元素的基线对齐到父元素的基线之上的给定长度,数值正值是基线往上偏移,负值是往下偏移,借此可以实现元素垂直方向精确对齐。)

百分比:20% (使元素的基线对齐到父元素的基线之上的给定百分比,该百分比是line-height属性的百分比。)

vertical-align属性起作用的前提必须是作用在内联元素上。display计算值为inline inline-block inline-table table-cell的元素。所以如果元素设置了float: left或者position: absolute,则其vertical-align属性不能生效,因为此时元素的display计算值为block了。

有时候会遇见下面这样高度和设置不一致的情况:

640?wx_fmt=png

div的实际高度比设定的行高大了,为什么呢?

内联元素的默认对齐方式是baseline,所以此时此时span元素的基线是和父元素的基线相对齐的,而此时父元素的基线在哪呢?

父元素的基线其实就是行框盒子前的幽灵空白节点的基线。把幽灵空白节点具象化为字母x可能容易理解些:

由于div行高是30px,所以字母xspan元素的高度都是30px。但是字母x的font-size较小,span元素的font-size较大,而行高一样的情况下font-size越大基线的位置越偏下,所以两者的基线不在同一水平线上。如下图左边部分:

640?wx_fmt=jpeg

由于内联元素默认基线对齐,所以字母xspan元素发生了位移以使基线对齐,导致div高度变大。而此时字母x的半行距比span元素的半行距大,大出的部分就是div的高度增加的部分。

先看例子,图中span元素设置了display: inline-block和宽高,从而撑起了父元素div的高度,但span本身并无margin属性,那为什么底部和div下边缘之间会有空隙呢?地址

640?wx_fmt=png

这就要说到inline-block的不同之处了。一个设置了display: inline-block的元素:

  1. 如果元素内部没有内联元素,则该元素基线就是该元素下边缘;

  2. 如果元素设置了overflowhidden auto scroll,则其基线就是该元素下边缘;

  3. 如果元素内部还有内联元素,则其基线就是内部最后一行内联元素的基线。

知道了这点,那么再回到上面的例子:

原来是第三者幽灵空白节点搞的鬼。此时span的行框盒子前,还存在一个幽灵空白节点。由于span元素默认基线对齐,所以span元素的基线也就是其下边缘是和幽灵空白节点的基线对齐的。从而导致幽灵空白节点基线下面的半行距撑高了div元素,造成空隙。如下图:

640?wx_fmt=png

如果span元素中存在内联元素呢?

640?wx_fmt=png

可以看到,此时span元素下边缘的空隙没了,因为此时span元素的基线是内部最后一行内联元素的基线。

值得一提的是,由于替换元素内部不可能再有别的元素,所以其基线位置永远位于下边缘。

间隙产生本质上是由基线对齐引发的错位造成的,源头上是vertical-alignline-height共同造成的,所以要想解决这个问题,只要直接或间接改造两个属性中的一个就行了:

  1. 给元素设置块状化display: block使vertical-align属性失效;

  2. 尝试不同的vertical-align值如bottom/middle/top

  3. 直接修改line-height值;

  4. 如果line-height为相对值如1.4,设置font-size: 0间接改变line-height

下面是张鑫旭大佬推荐的利用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>
  <div class="dialog">自适应弹出层</div>
</div>
<style>
.container{
  position: fixed;
  top0right0bottom0left0;
  background-colorrgba(0, 0, 0, .15);
  text-align: center;
  font-size0;
  white-space: nowrap;
  overflow: auto;
}
.container:after{
  content'';
  display: inline-block;
  height100%;
  vertical-align: middle;
}
.dialog{
  display: inline-block;
  width400px;
  height400px;
  vertical-align: middle;
  text-align: left;
  font-size14px;
  white-space: normal;
  background: white;
}
</style>


流的破坏

现在UI框架横行的年代,我们的css写的越来越少了。这对于很多老鸟来说是件好事,但是对于初入前端的小白却未必。因为写的少了,就少了很多练手和总结的机会,对于很多样式理解就不透彻。本章介绍的floatpositionBFC对于前端页面布局非常重要,希望诸位看官们静下心来仔细研读。

float属性的特性

float属性应该是css世界最令人意外的属性了,倒不是因为他的表现,而是他的设计初衷竟然只是为了实现“文字环绕图片”的效果。只不过因为float属性的一些特性,才导致其被到处使用以致于产生了诸多不利于维护的页面。
下面看看float属性的特性:

  1. 包裹性:即此时元素width会像height一样由子元素决定,而不是默认撑满父元素。

  2. 块状化并格式化上下文:这个就是后面会讲的BFC特性。块状是指元素设置float: left之后,其display的计算值就成了block。格式化上下文是指会创建一个BFC,这个后面会讲。

  3. 没有任何margin合并;

  4. 脱离文档流:float设计的初衷就是为了“文字环绕”效果,为了让文字环绕图片,就需要具备两个条件。第一是元素高度坍塌,第二是行框盒子不可与浮动元素重叠。而元素高度坍塌就导致元素后面的非浮动块状元素会和其重叠,于是他就像脱离文档流了。

前三个特性都是正能量满满,但是最后一个特性却给我们开发者带来了很多麻烦,需要用到clear来清除浮动。

clear的作用和不足

大家都知道clear: both可以清除前面浮动元素的浮动,但实际上,他并不是真的清除了浮动。

clear的定义是:元素盒子的边不能与前面的浮动元素相邻。也就是虽然浮动元素高度坍塌,但是设置了clear: both的元素却将其高度视为仍然占据位置。

clear只能作用于块级元素,并且其并不能解决后面元素可能发生的文字环绕问题。

BFC:块级格式化上下文

BFC是一个独立的渲染区域,只有Block-level box参与, 它规定了内部的Block-level Box如何布局,并且与这个区域外部毫不相干。
BFC 就好像一个结界,结界里面的东西不能影响外面的布局,也就是说,BFC的子元素再翻江倒海,都不会影响外面的元素。 所以:

  1. BFC本身不会发生margin重叠。

  2. BFC可以彻底解决子元素浮动带来的的高度坍塌和文字环绕问题。

  1. 根元素;

  2. 浮动元素 (float不为none的元素);

  3. 绝对定位元素 (元素的positionabsolutefixed);

  4. inline-blocks(元素的 display: inline-block);

  5. 表格单元格(元素的display: table-cellHTML表格单元格默认属性);

  6. overflow的值不为visible的元素;

  7. 弹性盒 flex boxes (元素的display: flexinline-flex);

BFC包含创建该上下文元素的所有子元素,但不包括创建了新BFC的子元素的内部元素。

  1. 内部的盒会在垂直方向一个接一个排列(可以看作BFC中有一个的常规流);

  2. Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠;

  3. 每一个盒子的左外边距应该和包含块的左边缘相接触。即使存在浮动也是如此,除非子盒子形成了一个新的BFC。

  4. BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之亦然;

  5. 计算BFC的高度时,考虑BFC所包含的所有元素,连浮动元素也参与计算;

  6. 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).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 就是普通的流布局和定位布局默认贴着“左侧”思想的总结。

640?wx_fmt=png

如图,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宽度是自适应的。

640?wx_fmt=png

绝对定位position: absolute

和浮动元素一样,绝对定位也具有块状化、BFC、包裹性、脱离文档流、没有margin合并的特性。

但和浮动不同的是,绝对定位是完全的脱离文档流。大家还记得浮动产生的目的就是为了实现文字环绕效果,所以浮动元素虽然脱离了文档流,但是后面的文字还是会环绕在浮动元素周围。而绝对定位一但产生,就不会再对周围元素产生任何影响。

而且两者包含块不同,浮动元素包含块只能是父级元素,绝对定位的包含块则是距离最近的position不为static的祖先元素。

大多数用到绝对定位的时候,都是存在包含块和left/top等方向属性的。但其实position: absolute是非常独立的css属性,其样式和行为表现不依赖任何css属性就可以完成。

640?wx_fmt=png

可以看出,无依赖的position: absolute元素定位的位置和其本身无定位属性时候的位置和display的值有关。如果元素在没有position的情况下是内联元素,则和内联元素在同一行显示;如果元素在没有position属性的情况下是块级元素,则换行显示。

无依赖绝对定位的实用性虽然还行,但是其功能却完全可以用left/top实现。所以了解即可,如果有兴趣可以自行尝试。

其实一句话就可以表示两者之间的关系:当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>
  <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>
  <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>

当绝对定位元素的水平方向(left/right)或垂直方向(top/bottom)的两个定位属性同时存在的时候,绝对元素在该方向上便具有了流体特性。此时的width/height属性具有自动撑满的特性,和一个正常流的div元素的width属性别无二致。如图,设置了固定margin值的元素,宽高auto能够自动适应剩余空间:

640?wx_fmt=png

同样的,设置了固定宽高的元素,如果margin: auto,则margin平分剩余空间导致垂直水平居中:

640?wx_fmt=png


层叠规则

层叠规则是指当网页中的元素发生层叠时侯的遵循的规则。

层叠上下文

层叠上下文好像是一个结界,层叠上下文内的元素如果跟层叠上下文外的元素发生层叠,则比较该层叠上下文和外部元素的层叠上下文的层叠水平高低。

创建一个层叠上下文的方法就是给position值为relative/aboslute/fixed的元素设置z-index不为auto的值。

层叠上下文内元素的层叠水平如下图:

640?wx_fmt=png
  1. 最底层的border/background是指当前层叠上下文元素的边框和背景色。z-index为负值的元素在其之上。

如下图所示.dad元素默认设置z-index: auto,没有创建层叠上下文,此时其就是一个普通的块级盒子,所以设置了z-index: -1.son元素跑到了爸爸身后看不见了。

而由于.mom设置了z-index: 0,创建出了一个层叠上下文,所以.son元素就算设置了z-index: -1也跑不出老妈的视线。地址

640?wx_fmt=png
  1. 当块级元素和内联元素发生层叠,内联元素居于块级元素之上。如下图:地址

640?wx_fmt=png
  1. 普通定位元素层叠水平在普通元素之上。普通定位元素是指z-indexauto的定位元素。下图span就是普通定位元素:地址

640?wx_fmt=png

CSS3新增层叠上下文

CSS3带来了很多新属性,其中很不惹人注意的一点就是增加了很多会自动创建层叠上下文的属性:

  1. 元素的opacity值不为1,也就是透明元素;

  2. 元素的transform值不为none

  3. 元素的filter值不为none

  4. 元素的设置-webkit-overflow-scrolling: touch

  5. z-index不为auto的弹性盒子的子元素;

  6. 元素的isolation值为isolate

  7. 元素的mix-blend-mode值不为normal

  8. 元素的will-change值为opacity/transform/filter/isolation/mix-blend-mode中的一个。

这些属性大都不支持z-index,所以他们都默认z-index: auto,跟普通定位元素层叠水平一样,所以如果发生层叠会后来居上:地址

640?wx_fmt=png

但是弹性盒子display: flex不同,弹性盒子的子元素支持设置z-index,且设置了数值的z-index也会自动创建层叠上下文。如下图,可以看到设置了z-index: 0的元素层叠水平更高。地址

640?wx_fmt=png


弹性布局

弹性布局是指display: flexdisplay: inline-flex的布局。注意,设为弹性布局以后,子元素的float、clear、vertical-align属性都会失效。参见阮一峰大佬的 Flex 布局教程。

主要属性应用如下:

640?wx_fmt=png


网格布局

网格布局(Grid)是最强大的 CSS 布局方案。注意,设为网格布局以后,容器子元素(项目)的float、display: inline-block、display: table-cell、vertical-align和column-*等设置都将失效。参见阮一峰大佬的CSS Grid 网格布局教程。

640?wx_fmt=png


文本控制

以下css属性为文本相关。

::first-letter 应用实例

::first-letter选中首个字符:地址

640?wx_fmt=png

text-transform 应用

假设有个输入框只能输入大写字母,那么如下设置,输入小写字母出现的却是大写字母,可用于身份证输入框或验证码输入框等:

  input {    text-transform: uppercase;  }
    text-transform: uppercase;
  }

word-spacing 空格间隙

不要被表面意思误导,word-spacing指的是字符“空格”的间隙。如果一段文字中没有空格,则该属性无效。下面代码设定空格间隙是20px,也就是说空格现在占据的宽度是原有的空格宽度+20px的宽度:

<p>我有空 格,我该死......</p><style>  p {    word-spacing: 20px;  }</style></p>
<style>
  p {
    word-spacing20px;
  }
</style>

white-space  空白处理

我们都知道如果在html中输入多个空白符,默认会被当成一个空白符处理,实际上就是这个属性控制的:地址

  1. normal:合并空白符和换行符;

  2. nowrap:合并空白符,但不许换行;

  3. pre:不合并空白符,并且只在有换行符的地方换行;

  4. pre-wrap:不合并空白符,允许换行符换行和文本自动换行;

640?wx_fmt=png

text-align: justify(本文重点例子!)

text-align: justify为两端对齐。除了实现文字的两端对齐,还能用来做一些两端对齐的布局。(注意下面例子自己测试时需要保证每行三个方块!!!)下面介绍个两端对齐布局的实例:地址

640?wx_fmt=png

由于text-align: justify最后一行是左对齐,所以利用了三个空的i标签模拟最后一行。虽然实现了两端对齐,但是最后一行却出现间隙。根据之前的经验应该是vertical-alignline-height搞的鬼,我们给i标签加上outline并用字母 x 模拟幽灵空白节点,现形:地址

640?wx_fmt=png

上图分析:首先第一个i标签基线与第二行的span标签中的数字的基线对其,所以其位置在中间。其次最后一行的i标签基线对齐幽灵空白节点字母x的基线,没有错位,所以此时最后一行的间隙高度就是字母x的高度。所以很容易想到把幽灵空白节点的行高设为 0 来解决问题:地址

640?wx_fmt=png

然而间隙虽然缩小了,但是还是存在。此时由于行高为 0 ,幽灵空白节点也就是字母x在页面中占用的真实位置其实是红线所示。也就是说虽然字母 x 还显示在页面上,但是其真实高度已经为0,此时其中线、上边缘线、下边缘线合一,都在红线位置,其真实位置自然也就在红线位置。然而其基线却不会改变,在字母 x 下边缘。

此时i标签的基线发生错位,位移到下面与幽灵空白节点基线对齐,导致产生了间隙。

所以只需要再改变i标签的对齐方式,就能彻底清除间隙:地址

640?wx_fmt=png

此时i标签的基线对齐其幽灵空白节点的下边缘线,没有了错位,也就没有了间隙。

如果改为vertical-align: top是一样的,因为合一了。但是vertical-align: middle却不行,因为此middle的位置是基线往上 1/2 个e-height的地方。

好吧本例结束了,没想到解释起来这么复杂。好好理解此例加深对vertical-alignline-height的理解。


元素的显示与隐藏

元素的显示隐藏方法很多,不同方法的在不同的场景下页面效果不一,对页面的性能也有不同的影响。

元素隐藏方法总结:

  1. 如果希望元素不可见、不占据空间、资源会加载、DOM 可访问:display: none

  2. 如果希望元素不可见、不能点击、但占据空间、资源会加载,可以使用:visibility: hidden

  3. 如果希望元素不可见、不占据空间、显隐时可以又transition淡入淡出效果:地址

div{  position: absolute;  visibility: hidden;  opacity: 0;  transition: opacity .5s linear;  background: cyan;}div.active{  visibility: visible;  opacity: 1;}
  position: absolute;
  visibility: hidden;
  opacity0;
  transition: opacity .5s linear;
  background: cyan;
}
div.active{
  visibility: visible;
  opacity1;
}

这里使用visibility: hidden而不是display: none,是因为display: none会影响css3的transition过渡效果。
但是display: none并不会影响cssanimation动画的效果。

  1. 如果希望元素不可见、可以点击、占据空间,可以使用:opacity: 0

  2. 如果希望元素不可见、可以点击、不占据空间,可以使用:opacity: 0; position: abolute;

  3. 如果希望元素不可见、不能点击、占据空间,可以使用:position: relative; z-index: -1;

  4. 如果希望元素不可见、不能点击、不占据空间,可以使用:position: absolute ; z-index: -1;

display: none与visibility: hidden的区别

  1. display: none的元素不占据任何空间,visibility: hidden的元素空间保留;

  2. display: none会影响css3的transition过渡效果,visibility: hidden不会;

  3. display: none隐藏产生重绘 ( repaint ) 和回流 ( relfow ),visibility: hidden只会触发重绘;(回流重绘知识点参考:真正理解重绘和回流)

  4. 株连性:display: none的节点和子孙节点元素全都不可见,visibility: hidden的节点的子孙节点元素可以设置 visibility: visible显示。visibility: hidden属性值具有继承性,所以子孙元素默认继承了hidden而隐藏,但是当子孙元素重置为visibility: visible就不会被隐藏。

如果面试问到这个问题,回答出来这四点应该是极好的。


  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值