1

一 前言

要想掌握CSS部分的内容其实并不容易,尽管你已经阅读过相当多的关于CSS的书籍,但是还是免不了去网上搜索相关的知识扩大你对CSS内容的掌握。在网络上查找最好的地方当然是CSS官网文档,不过英文版的官方文档将很大一部分人拒之门外,而中文版的博客上五花八门的只是零散又不权威,很容易将人带入歧途,浪费时间。笔者写这篇文章,摘录博客上总结的CSS知识,同时参考CSS官方文档,尽力保证其正确性。希望能节省开发者查阅的时间,提高效率。如有错误的地方,还请大家指出~一起学习,共同进步
适合:掌握CSS基础知识的开发者进阶、前端从业人员速查手册、面试人员必备

二 正文

DOM脱离文档流

什么是文档流

指语言文本从左到右,从上到下显示,这是传统HTML文档的文本布局。
脱离文档流即是元素打乱了这个排列,或是从排版中拿走。

什么情况下会脱离文档流

浮动(float)、绝对定位(absolute)、固定定位(fixed)三种方式定位会脱离文档流。

浮动

浮动元素会脱离文档流并向左/向右浮动,直到碰到父元素或者另一个浮动元素。float 属性定义元素在哪个方向浮动。以往这个属性总应用于图像,使文本围绕在图像周围,不过在 CSS 中,任何元素都可以浮动。

特点:

  1. 浮动元素会生成一个块级框,而不论它本身是何种元素
  2. 如果浮动非替换元素,则要指定一个明确的宽度;否则,它们会尽可能地窄
  3. 没有实际高度,浮动会导致父元素高度坍塌

需要注意:
向左浮动,方块从右往左滑过来,如果某一行空间不够(基于父容器的宽度),那么这个块会沿着最右边的块的下边沿水平划过来,最后看卡到哪里就停止。
图片描述

left4在第一行left3后面宽度不够,换行成第二行。在left2后面宽度不够,只能在left1后面。right1自然是从第二行开始查找位置,很幸运第二行现在唯一的元素#left4右面空间足够,放置即可。right2在第二行中没有足够的位置,换行成第三行,找到空余位置插入。

定位

图片描述
absolute的基准元素是 static 定位以外的第一个父元素,而fix的基准元素是浏览器窗口。position的值为absolute、fixed的元素脱离文档流,static、relative没有脱离文档流。

absolute定位结果

分两种情况来考虑:

第一种:元素的某条边没有设置定位属性(left/top/right/bottom)
这个时候,这一边就会将absolute块(包括外边距)按流式布局(正常布局)来排列,当然这个流式布局就会受到内边距padding的影响了。

<!DOCTYPE html>
<html>
  <head>
      <meta charset="utf-8">
      <!--<script type="text/javascript" src='jquery-1.9.1.js'></script>-->
      <style>
    *{
      margin: 0;
      padding: 0;
      text-align: right;
      color: #FFF;
    }
    #container{
      position: absolute;
      left: 20px;
      margin-top: 10px;
      width: 600px;
      height: 600px;
      background-color: green;
    }
    #big{
      position: relative;
      left: 20px;
      margin-top: 40px;
      width: 300px;
      height: 300px;
      background-color: #000;
    }
    #normal{
      position: static;
      margin-left: 20px;
      margin-top: 50px;
      width: 200px;
      height: 200px;
      background-color: #aaa;
    }
    #middle{
      position: absolute;
      margin-left:0px;
      margin-top:20px;
      width: 100px;
      height: 100px;
      background-color: #333;
    }
    </style>
  </head>
  <body>
    <div id="container">
          <div id="big">
            <div id="normal">
              <div id="middle">
                middle</div>normal
              </div>big
            </div> container
      </div>
    </div>
</body>
</html>

图片描述

第二种,元素的某条边设置了定位属性(left/top/right/bottom)
这时候,这一边就会被作为脱离文档流的边来处理,会相对于 static 定位以外的第一个父元素(的边框)进行定位,如果这时候设置了外边距,那么这一边相对于基准元素的偏移=定位值(left/top/right/bottom)+ 外边距。

<!DOCTYPE html>
<html>
  <head>
      <meta charset="utf-8">
      <!--<script type="text/javascript" src='jquery-1.9.1.js'></script>-->
      <style>
    *{
      margin: 0;
      padding: 0;
      text-align: right;
      color: #FFF;
    }
    #container{
      position: absolute;
      left: 20px;
      margin-top: 10px;
      width: 600px;
      height: 600px;
      background-color: green;
    }
    #big{
      position: relative;
      left: 20px;
      margin-top: 40px;
      width: 300px;
      height: 300px;
      background-color: #000;
    }
    #normal{
      position: static;
      margin-left: 20px;
      margin-top: 50px;
      width: 200px;
      height: 200px;
      background-color: #aaa;
    }
    #middle{
      position: absolute;
      left:0px;
      margin-left:0px;
      margin-top:20px;
      width: 100px;
      height: 100px;
      background-color: #333;
    }
    </style>
  </head>
  <body>
    <div id="container">
          <div id="big">
            <div id="normal">
              <div id="middle">
                middle</div>normal
              </div>big
            </div> container
      </div>
    </div>
</body>
</html>

图片描述

对于浮动与定位的位置优先级:
(fixed == absolute) > float > relative > static
理解为同时设置了position:absolute和float:right忽略float:right
堆叠优先级:

1. 定位元素z-index>=0时为(fixed == absolute == relative)  > static,z-index < 0时(fixed == absolute == relative)  < static。
理解为兄弟元素一个设置了position:relative,另一个设置了position:static,当relative元素z-index为>=0时,无论static元素设置多少都在relative下面。如果relative设置z-index<0,则论static取什么值relative都会在static下面。

2. 定位元素z-index>=0时(fixed == absolute == relative)  > float,z-index < 0时(fixed == absolute == relative)  < float。
理解为兄弟元素一个设置了position:relative,另一个设置了float:left,当relative元素z-index为>=0时,无论float元素设置多少都在relative下面。如果relative设置z-index<0,则无论float的z-index设置多少relative都会在float下面。

3. float > static始终成立。因为z-index只有在定位元素上才起作用,定位元素包括fixed、absolute、relative。

4. fixed == absolute == relative,比较堆叠顺序直接比较z-index大小即可。

参考资料:
【1】浮动:https://www.cnblogs.com/chuaW...
【2】定位:https://segmentfault.com/a/11...

垂直边距合并

什么是垂直边距合并

当两个垂直外边距相遇时,它们将形成一个外边距。合并后的外边距的高度等于两个发生合并的外边距的高度中的较大者。

什么情况下会发垂直边距合并

1. 只针对块级元素
2. 正常文档流

注意:合并行为只适用于外边距,如果元素有内边距和边框,它们绝对不会合并。
计算方法【待研究确认!】:
// 相对方向
一个正一个负:正边距减去这个负边距绝对值
两个负数: 取绝对值较大的负数。-30px与-20px折叠,折叠后为-30px。
// 相同方向
绝对值最大的那个值
情况1:同一层级垂直方向上盒子的相遇
<html>
<head>

<style type="text/css">
* {
  margin:0;
  padding:0;
  border:0;
}

#d1 {
  width:100px;
  height:100px;
  margin-top:20px;
  margin-bottom:20px;
  background-color:red;
}

#d2 {
  width:100px;
  height:100px;
  margin-top:10px;
  background-color:blue;
}

</style>
</head>

<body>

<div id="d1">
</div>

<div id="d2">
</div>

<p>请注意,两个 div 之间的外边距是 20px,而不是 30px(20px + 10px)。</p>
</body>
</html>

图片描述

(注意:图片中的“内容区域”应该是包括content和padding)
demo1

情况2:存在嵌套关系

 父子节点都是没有脱离文档的两种定位(static、relative)的外边距(margin)会合并,显示效果以最大的那个外边距为准:父元素的第一个子元素的上边距margin-top如果碰不到有效的border或者padding.就会不断一层一层的找自己“领导”(父元素,祖先元素)的麻烦。

没有给父元素设置内边距或者有效的边框的情况下:

<html>
<head>
<style type="text/css">
* {
  margin:0;
  padding:0;
  border:0;
} 
#outer {
  width:300px;
  height:300px;
  background-color:red;
  margin-top:20px;
} 
#inner {
  width:50px;
  height:50px;
  background-color:blue;
  margin-top:10px;
}
</style>
</head>
<body>
<div id="outer">
  <div id="inner">
  </div>
</div>
<p>注释:请注意,如果不设置 div 的内边距和边框,那么内部 div 的上外边距将与外部 div 的上外边距合并(叠加)。</p>
<p>为父元素例子中的middle元素增加一个border-top或者padding-top即可</p>
</body>
</html>

图片描述

边距合并实际例子:

<!DOCTYPE html>
<html>
  <head>
      <meta charset="utf-8">
      <!--<script type="text/javascript" src='jquery-1.9.1.js'></script>-->
      <style>
    *{
      margin: 0;
      padding: 0;
      text-align: right;
      color: #FFF;
    }
    #container{
      position: absolute;
      left: 20px;
      margin-top: 10px;
      width: 600px;
      height: 600px;
      background-color: green;
    }
    #big{
      position: relative;
      left: 20px;
      margin-top: 40px;
      width: 300px;
      height: 300px;
      background-color: #000;
    }
    #normal{
      position: static;
      margin-left: 20px;
      margin-top: 50px;
      width: 200px;
      height: 200px;
      background-color: #aaa;
    }
    #middle{
      position: absolute;
      margin-left:0px;
      margin-top:20px;
      width: 100px;
      height: 100px;
      background-color: #333;
    }
    </style>
  </head>
  <body>
    <div id="container">
          <div id="big">
            <div id="normal">
              <div id="middle">
                middle</div>normal
              </div>big
            </div> container
      </div>
    </div>
</body>
</html>

图片描述

结果:normal和big都没有脱离文档流,发生嵌套时,最终确认上边距为50px.

解决方案:在包含块上设置边框或者内边距,会使其子元素的外边距包含在包含块内。

解决这种情况下边距合并:

<!DOCTYPE html>
<html>
  <head>
      <meta charset="utf-8">
      <!--<script type="text/javascript" src='jquery-1.9.1.js'></script>-->
      <style>
    *{
      margin: 0;
      padding: 0;
      text-align: right;
      color: #FFF;
    }
    #container{
      position: absolute;
      left: 20px;
      margin-top: 10px;
      width: 600px;
      height: 600px;
      background-color: green;
    }
    #big{
      padding-top:5px;
      position: relative;
      left: 20px;
      margin-top: 40px;
      width: 300px;
      height: 300px;
      background-color: #000;
    }
    #normal{
      position: static;
      margin-left: 20px;
      margin-top: 50px;
      width: 200px;
      height: 200px;
      background-color: #aaa;
    }
    #middle{
      position: absolute;
      margin-left:0px;
      margin-top:20px;
      width: 100px;
      height: 100px;
      background-color: #333;
    }
    </style>
  </head>
  <body>
    <div id="container">
          <div id="big">
            <div id="normal">
              <div id="middle">
                middle</div>normal
              </div>big
            </div> container
      </div>
    </div>
</body>
</html>

图片描述

注意:#big{ padding-top:5px;} 这里normal上边框距离big上边框的值应该是:margin-top(normal) + padding-top(big)

情况3.外边距与自身发生合并

假设有一个空元素,它有外边距,但是没有边框或填充。在这种情况下,上外边距与下外边距就碰到了一起,它们会发生合并:
图片描述

如果这个外边距遇到另一个元素的外边距,它还会发生合并:

图片描述
这就是一系列的段落元素占用空间非常小的原因,因为它们的所有外边距都合并到一起,形成了一个小的外边距。

外边距计算demo

// html
<div>
    <ul>
        <li>a list item</li>
        <li>another list item</li>
    </ul>
    <H1>A Heading-1</H1>
</div>
// style
li{margin-bottom:20px;background-color:yellow;}
ul{margin-bottom:-15px;background-color:gray;}
h1{margin-top:-18px;background-color:orange;}

image.png
两个负边距中较大的一个(-18px)增加到了最大正边距上(20px),这就得到了20px - 18px = 2px。因此列表项内容底端与H1内容顶端只有2px像素的距离。
参考资料:
【1】http://www.w3school.com.cn/cs...

BFC

BFC(Block Formatting Contexts)直译为"块级格式化上下文"。Block Formatting Contexts就是页面上的一个隔离的渲染区域,容器里面的子元素不会在布局上影响到外面的元素,反之也是如此。

BFC形成

  1. float 的值不为 none
  2. position 的值不为 static 或 relative
  3. display属性为inline-boxs、table-cells、table-captions的不是块盒的块容器(除非这个值已经被传播到视口),
  4. overflow不为visible的块盒才会为它的内容创建新的BFC。
  5. body元素

BFC渲染规则

  1. 内部的Box会在垂直方向,一个接一个地放置。
  2. Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠
  3. 每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
  4. BFC的区域不会与float box重叠,常用来清除浮动和布局。
  5. BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
  6. 计算BFC的高度时,浮动元素也参与计算

BFC的作用及原理

  1. 自适应两栏布局
  2. 清除内部浮动
  3. 防止垂直 margin 重叠

BFC内部的元素和外部的元素绝对不会互相影响

  1. 当BFC外部存在浮动时,它不应该影响BFC内部Box的布局,BFC会通过变窄,而不与浮动有重叠【自适应两栏布局】
  2. 当BFC内部有浮动时,为了不影响外部元素的布局,BFC计算高度时会包括浮动的高度【清除内部浮动】
  3. 避免margin重叠道理和2相同【防止垂直 margin 重叠】

具体可参考:https://juejin.im/post/5a4dbe...

IFC

IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)

IFC渲染规则

  1. 框会从包含块的顶部开始,一个接一个地水平摆放。
  2. 摆放这些框的时候,它们在水平方向上的外边距、边框、内边距所占用的空间都会被考虑在内。
  3. 在垂直方向上,这些框可能会以不同形式来对齐:它们可能会把底部或顶部对齐,也可能把其内部的文本基线对齐。能把在一行上的框都完全包含进去的一个矩形区域,被称为该行的行框。
  4. 水平的margin、padding、border有效,垂直无效。
  5. 不能指定宽高。
  6. 行框的宽度是由包含块和存在的浮动来决定。

IFC ‘line-height’ 与 ‘vertical-align’ 属性

计算行框里的各行内级框的高度:

  1. 行框的高是最顶端框的顶边到最底端框的底边的距离。
  2. 对于置换元素、行内块元素、行内表格元素来说,这是边界框的高度
  3. 对于行内框来说,这是其 ‘line-height’。

行内级元素根据其 ‘vertical-align’ 属性垂直对齐。
在这些框使用 ‘top’ 或 ‘bottom’ 对齐的情况下,user-agent必须以最小化行框的高为目标对齐这些框。若这些框够高,则存在多个解而不定义行框基线的位置。

margin:auto与width的计算

主要讲了margin与width结合处理大小为auto的问题,并详细介绍width的计算原则。
将这个问题之前先放一张盒模型示意图:

图片描述

现在有一个宽度为400px的包含块,针对如图所示:

图片描述

只有三个属性可以设置为auto:width/margin-left/margin-right

情况一:margin-left margin-right width的值都是auto
表 现:width尽可能的大,width的值为100%;margin-left margin-right均为0

情况二:margin-left margin-right width的值都是100
表 现:margin-right 被强制设置成200

情况三:margin-left margin-right 的值是auto;width已知为200px
表 现:居中显示

情况四:margin-left:-100, margin-right:100 width的值是auto
表 现:width被计算为400px

情况五:margin-left:100,width:100,margin-right的值都是auto
表 现:margin-right被计算为200px

垂直格式化

  1. 如果一个块级正常流元素的height设置为一个百分数,这个值则是包含块 height的一个百分数。
  2. 合并行为只适用于外边距,如果元素有内边距和边框,它们绝对不会合并。

文本溢出省略

单行文本

overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;

多行文本

WebKit内核的浏览器
实现起来比较简单,可以通过添加一个-webkit-line-clamp的私有属性来实现,-webkit-line-clamp是用来限制在一个块元素显示的文本的行数。 为了实现这个效果,它需要组合其他的WebKit属性:

  • display: -webkit-box将对象作为弹性伸缩盒子模型显示;
  • -webkit-box-orient设置或检索伸缩盒对象的子元素的排列方式;
  • text-overflow: ellipsis用省略号“…”隐藏超出范围的文本。

具体代码参考如下:

overflow : hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;

其他浏览器的解决方案
目前没有什么CSS的属性可以直接控制多行文本的省略显示,比较靠谱的办法应该就是利用相对定位在最后面加上一个省略号了,代码可以参考下面:

p {

position:relative;
line-height:1.5em;
/* 高度为需要显示的行数*行高,比如这里我们显示两行,则为3 */
height:3em;
overflow:hidden;

}
p:after {

content:"...";
position:absolute;
bottom:0;
right:0;
padding: 0 5px;
background-color: #fff;

}

三 后记

CSS相关链接:
CSS基础


specialcoder
2.2k 声望170 粉丝

前端 设计 摄影 文学