美文网首页
前端基础篇之CSS世界

前端基础篇之CSS世界

作者: 程序猿TODO | 来源:发表于2021-01-13 14:42 被阅读0次

    我想你每天写css代码有时候也会觉得很痛苦:这个布局的css怎么这么难实现!我也经常会有这种感觉,一个看似简单的布局总是要琢磨半天才能实现,偶尔还会出现一些怪异的超出理解的现象。这是因为我们对css只是大概知道个形,并没有看透css的本质。在同事的推荐下我阅读了张鑫旭老师的《css世界》,才发现css跟想象中的不太一样。本文为《css世界》个人总结笔记,为缩减篇幅丢弃了张老师冗余的小幽默,丢掉了些含金量较低的章节内容,因为ie已经被淘汰出局,所以有关css兼容性的地方也全部忽略不记,同时对个人觉得不易理解的地方加上了一些自己的理解和验证,所以错误之处还望指正。顺便推荐个好用的在线代码编辑工具,国内镜像站点,方便各位对本文实例进行测试。另外本文会随着作者对css的更深入理解而逐步更新,希望到最后能够文如标题展现出真正的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: 100%的固定宽度,而是像水一样能够根据margin不同而自适应父元素的宽度。

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

    无论内联元素还是块级元素,height: auto都是呈现包裹性,即高度由子级元素撑开。

    注意父元素height: auto会导致子元素height: 100%百分比失效。

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

    外在盒子和内在盒子

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

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

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


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

    css权重和超越!important

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

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

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

    css选择器权重列表如下:

    权重值 选择器
    1,0,0,0 内联样式:style=""
    0,1,0,0 ID选择器:#idName{...}
    0,0,1,0 类、伪类、属性选择器:.className{...} / :hover{...} / [type="text"] ={...}
    0,0,0,1 标签、伪元素选择器:div{...} / :after{...}
    0,0,0,0 通用选择器(*)、子选择器(>)、相邻选择器(+)、同胞选择器(~)

    以前想当然的以为权重是十进制的,即十个class选择器等于一个id选择器,这是错误的。CSS权重进制在IE6为256,后来扩大到了65536,而现代浏览器则采用更大的数量。所以我们可以忽略进制的问题,从高往低比较权重值。

    如果权重是十进制的比较方式,则十一个class选择器的权重高于一个ID选择器。但经测试并非如此:地址


    可以看到十一个class选择器的样式并没有覆盖一个id选择器的样式,因为:

    当两个权值进行比较的时候,是从高到低逐级将等级位上的权重值来进行比较的,而不是 1000个数 + 100个数 + 10个数 + 1个数 的总和来进行比较的,换句话说,低等级的选择器个数再多也不会超过高等级的选择器的优先级的。

    正确规则:

    1. 先从高等级进行比较,高等级相同时,再比较低等级的,以此类推;
    2. 完全相同的话,就采用 后者优先 原则;

    因此上面那道的面试题比较应该是在第二等级id选择器的比较就结束了:(#god + #test = 0,2,0,0) > (#god = 0,1,0,0);而上图种例子中两个权重分别是:(#test = 0,1,0,0) > (.test....test10 = 0,0,11,0),也是在第二等级id选择器的比较时就结束了。所以以后比较权重,就先比较id选择器个数,如果id一样多,再比较class选择器个数。哇,感觉这可以是一道专坑中文社区的面试题啊。

    在css中,!important的权重相当的高。如果出现了!important,则不管权重如何都取有!important的属性值。但是宽高有例外情况,由于宽高会被max-width/min-width覆盖,所以!important会失效。

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

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

    width: 200px;
    

    盒模型(盒尺寸)

    元素的内在盒子是由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;
    }
    

    内联盒模型

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

    1. 内容区域:本质上是字符盒子。在浏览器中,文字选中状态的背景色就是内容区域。
    2. 内联盒子:内联盒子就是指元素的外在盒子是内联的,会和其他内联盒子排成一行。
    3. 行框盒子:由内联元素组成的每一行都是一个行框盒子。如果一行里面没有内联元素如一个空的div标签,则不会形成行框盒子。行框盒子由一个个内联盒子组成,如果换行,那就是两个行框盒子。比如一个包含了很多字符的换行的的p标签,每一行都存在一个行框盒子。值得注意的是,如果给元素设置display: inline-block,则创建了一个独立的行框盒子。line-height是作用在行框盒子上的,并最终决定高度。
    4. 包含盒子:就是包含块。多行文字组成一个包含块,一个包含块有若干个行框盒子。
    5. 幽灵空白节点:内联元素的每个行框盒子前面有一个“空白节点”,这个“空白节点”不占据任何宽度,无法选中获取,但是又实实在在存在,表现就如同文本节点一样(本文中大量例子会用字母x模拟幽灵空白节点)。

    替换元素

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

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

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

    css的继承机制

    参见css简单的继承

    盒模型四大金刚

    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的。
    1. padding不可为负值,但是可以为百分比值。为百分比时水平和垂直方向的padding都是相对于父级元素宽度计算的。将一个div设为padding: 100%就能得到一个正方形,padding: 10% 50%可以得到一个宽高比 5:1 的矩形。
    body {
      width: 400px;
    }
    .box {
      padding: 10% 50%;
    }
    
    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;
    }
    

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


    margin

    1. 作为外边距,margin属性并不会参与盒子宽度的计算,但通过设置margin为负值,却能改变元素水平方向的尺寸:
    <div>asdf</div>
    <style>
      div {
        margin: 0 -100px;
      }
    </style>
    

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

    1. 块级元素的垂直方向会发生margin合并,存在以下三种场景:
    • 相邻兄弟元素之间margin合并;
    • 父元素margin-top和子元素margin-top,父元素margin-bottom和子元素margin-bottom
    • 空块级元素自身的margin-topmargin-botom合并,例子如下。

    要阻止margin合并,可以:1. 把元素放到bfc中;2. 设置borderpadding阻隔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>
    

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

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

    1. 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-heightvertical-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

    • 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-heightfont-size。行距具有上下等分的机制:意思就是文字上下的行距是一样的,各占一半,这也是line-height能让内联元素垂直居中的原因。下图中字母x上下行距各占一半,共同撑起了div

    下图中和上图唯一不同之处就是多了个display: inline-blockspan元素,但是此处的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 bottombaseline使元素的基线与父元素的基线对齐,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-heightvertical-align和第三者幽灵空白节点的爱恨情仇

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


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

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

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

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

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

    • display: inline-block基线的不同之处

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

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

    1. 如果元素内部没有内联元素,则该元素基线就是该元素下边缘;
    2. 如果元素设置了overflowhidden auto scroll,则其基线就是该元素下边缘;
    3. 如果元素内部还有内联元素,则其基线就是内部最后一行内联元素的基线。

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

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

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

    可以看到,此时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
    • 弹框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写的越来越少了。这对于很多老鸟来说是件好事,但是对于初入前端的小白却未必。因为写的少了,就少了很多练手和总结的机会,对于很多样式理解就不透彻。本章介绍的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可以彻底解决子元素浮动带来的的高度坍塌和文字环绕问题。
    • BFC的创建方法
    1. html根元素
    2. 浮动元素,float不是none就行
    3. 绝对定位元素,absolute和fixed
    4. display不是block的块(inline-block、table-cell、table-caption)
    5. overflow 值不为 visible 的元素(hidden、auto、scroll)
    6. display为flex、grid、flow-root的元素(flow-root为专门设置BFC的属性值)

    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).

    网上很多翻译成“每个元素的margin box的左边, 与包含块border box的左边相接触”的,这样的翻译是不准确甚至错误的,曾给我造成莫大的困惑和迷茫(这翻译太坑爹了一度让我怀疑人生)。正确的翻译是“每一个盒子的左外边距应该和包含块的左边缘相接触”。

    第一,包含块未必就是父级元素。对于position: absolute来说,包含块是指第一个positoin不为static的祖先元素。

    第二,BFC中的盒子应该与其自身的包含块相接触,而非与BFC盒子本身相接触。

    第三,BFC中的盒子是与其包含块的 left edge 相接触,而不是包含块的 left-border 相接触。left edge 正确的翻译为左边缘。左边缘可能是content box的左边缘(非绝对定位如position: relative float: left),也可能是padding box的左边缘(如绝对定位position: absolute position: fixed)。

    理解了上面三点,其实特性 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 实现自适应两栏布局。此时main宽度是自适应的。

    除了 BFC ,还有 IFC GFC FFC 等布局,了解即可,想看点 这里

    绝对定位 position: absolute

    • 定义

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

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

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

    • 默认最大宽度为包含块的宽度

    这个特性看起来很正常,因为正常的元素默认最大宽度都是包含块的宽度。


    由于这个特性,如果包含块的宽度很小,会出现一柱擎天的现象。解决方法也很简单,加个white-space: nowrap让文本不换行就行了:地址
    • 无依赖绝对定位

    大多数用到绝对定位的时候,都是存在包含块和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: fixed

    position: fixed 是相对于屏幕视口的位置来指定元素位置,祖先元素设置 position: relative 并不会对其产生影响。

    position: fixed 只有一个要注意的点,那就是当元素祖先的 transform 属性非 none 时,容器由视口改为该祖先:地址

    粘性定位 position: sticky

    #one { position: sticky; top: 10px; }
    复制代码
    

    在 viewport 视口滚动到元素 top 距离小于 10px 之前,元素为相对定位。之后,元素将固定在与顶部距离 10px 的位置,直到 viewport 视口回滚到阈值以下。

    利用粘性定位实现固定字母表头部元素:地址

    需注意当 position: sticky 的父元素的 overflow 属性设置了默认值 visible 以外的值时,position: sticky 将失效。

    position: sticky 除了不兼容 ie 浏览器,其他还好。

    层叠规则

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

    层叠上下文

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

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

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


    1. 最底层border/background是当前层叠上下文元素的边框和背景色(注意不包括文字,文字相当于内联盒子)。z-index为负值的元素在其之上。

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

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

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

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

    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,跟普通定位元素层叠水平一样,所以如果发生层叠会后来居上:地址

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

    文本控制

    以下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中输入多个空白符,默认会被当成一个空白符处理,实际上就是这个属性控制的:地址

    1. normal:合并空白符和换行符;
    2. nowrap:合并空白符,但不许换行;
    3. pre:不合并空白符,并且只在有换行符的地方换行;
    4. pre-wrap:不合并空白符,允许换行符换行和文本自动换行;


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

    text-align: justify为两端对齐(对尾行无效且子元素之间必须有空格或换行符)。除了实现文字的两端对齐,还能用来做一些两端对齐的布局。下面介绍个两端对齐布局的实例:地址

    由于text-align: justify最后一行是左对齐,所以利用了三个空的i标签模拟最后一行。虽然实现了两端对齐,但是最后一行却出现间隙。根据之前的经验应该是vertical-alignline-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的地方。

    好吧本例结束了,没想到解释起来这么复杂。虽然当下两端对齐布局使用这种方法已经过时了(没 flex 实现起来简单,更没 grid 实现好),但是好好学习这个方法能加深对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;
    }
    

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

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

    display: nonevisibility: 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就不会被隐藏。

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

    弹性布局

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

    主要属性应用如下:


    弹性布局实现两端对齐尾行左对齐:地址

    可以看到 .container 容器中加了很多 i 空元素,这是为了模拟项目占位,也是弹性布局实现两端对齐的唯一不足之处~

    网格布局

    网格布局是目前最强大的 CSS 布局方案。这里总结了用 Grid 实现的常用 css 布局:Grid网格布局实例

    更新说明

    • 更新了css权重和超越!important章节中权重部分的错误知识;(2019.10.16)
    • 更新了弹性布局实现两端对齐尾行左对齐实例;(2019.10.16)
    • 更新了text-align: justify链接中实例的显示,固定了容器宽度;(2019.10.16)
    • 新增了固定定位 position: fixed;(2019.11.7)
    • 新增了粘性定位 position: sticky;(2019.11.7)
    • 新增了默认最大宽度为包含块的宽度;(2020.3.27)
    • 更新了BFC的创建方法;(2020.4.23)

    相关文章

      网友评论

          本文标题:前端基础篇之CSS世界

          本文链接:https://www.haomeiwen.com/subject/kvbuaktx.html