Skip to content

CSS 知识集锦

作者:Atom
字数统计:12.4k 字
阅读时长:43 分钟

一、层叠上下文

层叠上下文,英文称作 stacking context,是 HTML 中的一个三维的概念。如果一个元素含 有层叠上下文,我们可以理解为这个元素在 z 轴上就“高人一等”。可以把层叠上下文理解为一种"层叠结界", 自成一个小世界

普通元素的层叠等级优先由其所在的层叠上下文决定。层叠等级的比较只有在当前层叠上下文元素中才有意义。不同层叠上下文中比较层叠等级是没有意义的。

层叠上下文创建

  • 根元素 <html> 创建的层叠上下文

  • postion 创建的层叠上下文

Postion 创建的层叠上下文

普通元素设置 position 属性为非 static 值并设置 z-index 属性为具体数值,产生层叠上下文。

  • CSS3 创建的层叠上下文

CSS3 创建的层叠上下文

  • 元素为 flex 布局元素(父元素 display:flex|inline-flex),同时 z-index 值不是 auto。
  • 元素的 opacity 值不是 1。
  • 元素的 transform 值不是 none。
  • 元素 mix-blend-mode 值不是 normal。
  • 元素的 filter 值不是 none。
  • 元素的 isolation 值是 isolate。
  • 元素的 will-change 属性值为上面 2~6 的任意一个(如 will-change:opacity、will-chang:transform 等)。
  • 元素的-webkit-overflow-scrolling 设为 touch。

注意

z-index: autoz-index: 0 两个数值上面是相等的, 但是意义却不一样; positionstaticz-index: 0 会产生层叠上下文, 而此时z-index: auto不产生

层叠特性及准则

层叠特性

  • 层叠上下文的层叠水平要比普通元素高
  • 层叠上下文可以阻断元素的混合模式
  • 层叠上下文可以嵌套,内部层叠上下文及其所有子元素均受制于外部的“层叠上下文”
  • 每个层叠上下文和兄弟元素独立,也就是说,当进行层叠变化或渲染的时候,只需要考虑后代元素
  • 每个层叠上下文自成体系,当元素发生层叠的时候,整个元素被认为是在父层叠上下文的层叠顺序中(通过父元素来比较)

黄金准则

下面这两条是层叠领域的黄金准则。当元素发生层叠的时候,其覆盖关系遵循下面两条准则:

  1. 谁大谁上:当具有明显的层叠水平标识的时候,如生效的 z-index 属性值,在同一个层叠上下文领域,层叠水平值大的那一个覆盖小的那一个。

  2. 后来居上:当元素的层叠水平一致、层叠顺序相同的时候,在 DOM 流中处于后面的元素会覆盖前面的元素。

元素层叠顺序

image.png

同一层叠上下文的层叠等级才能进行比较, 同一层叠上下文中的元素还需要比较层叠顺序来确定最终的层叠等级, 层叠顺序从小到大依次为:

  • background/border最小

注意

位于最下面的 background/border 特指当前层叠上下文元素的边框和背景色。每一个层叠顺序规则仅适用于当前层叠上下文元素的小世界

  • z-index 负值
  • block 块状水平盒子
  • float 浮动盒子
  • inline 水平盒子(包括inline/inline-block/inline-table元素)
  • z-index 为 auto 或者 css3 默认的层叠上下文
  • z-index 正值

注意

如果是普通元素, 一旦普通元素具有了层叠上下文,其层叠顺序就会变高。那它的层叠顺序究竟在哪个位置、哪个级别呢?

  • 如果层叠上下文元素不依赖 z-index 数值(不支持z-index),则其层叠顺序是 z-index:auto,可看成 z-index:0 级别;
  • 如果层叠上下文元素依赖 z-index 数值(定位元素),则其层叠顺序由 z-index 值决定。

层叠顺序是z-index:auto 级别,跟没有 z-index 值的 absolute 绝对定位元素是平起平坐的

z-index不二准则

对于非浮层元素,避免设置 z-index 值,z-index 值没有任何道理需 要超过 2

  1. 定位元素一旦设置了 z-index 值,就从普通定位元素变成了层叠上下文元素,相互 间的层叠顺序就发生了根本的变化,很容易出现设置了巨大的 z-index 值也无法覆盖其他元 素的问题。

  2. 避免 z-index“一山比一山高”的样式混乱问题。

层叠等级比较

Stacking Level

Stacking Level: 可译为层叠水平或层叠等级,决定了同一个层叠上下文中元素在 z 轴上的显示顺序

  • 首先比较两个元素是否处于同一个层叠上下文中

INFO

  1. 同一层叠上下文比较多个子元素层叠顺序
  2. 同一层叠上下文同一层叠顺序, 层叠等级大,显示在上面
  3. 不在同一层叠上下文中,请先比较他们所处的父级层叠上下文的层叠等级
  • 当两个元素层叠等级相同、层叠顺序相同时,在 DOM 结构中后面的元素层叠等级在前面元素之上。

二、百分比的相对性

em 相对于当前元素或当前元素继承来的字体的宽度, 对于 font-size 来说,em 相对于父元素的字体大小;line-height 中,em却相对于自身字体的大小

  • position: relative 中的百分比是相对于相对与自身的,left 相对于自己的 widthtop 相对于自己的 height

  • position: fixed 中的 的百分比是相对于视口的,left 相对于视口的 widthtop 相对于视口的 height

  • marginpadding 的百分比是相对于父元素的宽度

  • border-radius 的百分比是相对于自身宽高

  • background-size 的百分比是相对于自身宽高

  • transform: translate的百分比是相对于自身宽高

  • text-indent 的百分比是相对于父元素的宽度width

  • font-size 的百分比是相对于父元素的字体font-size数值

  • line-height 的百分比是相对于该元素的font-size数值

三、行高继承规则

父元素设置了line-height, 子元素应该如何继承, 主要分为以下三种情况:

  • 父元素的line-height是具体数值, 那么子元素直接继承该值

  • 父元素的line-height是比例值(如: 1.5), 则继承该比例值, 再根据子元素的font-size大小计算

  • 父元素的line-height是百分比(如: 200%), 则先通过父元素font-size计算line-height具体值后再继承

四、格式上下文

CSS中的格式化上下文有很多种,除了BFC、IFC之外还有由Flexbox布局创建的FFC和Grid布局创建GFC等。这些统称为CSS格式化上下文,也被称作视觉格式化模型。而CSS视觉格式化模型是用来处理文档并将它显示在视觉媒体上的机制。简单地说,就是用来控制盒子的位置,即实现页面的布局

格式化上下文也可以说是CSS视觉渲染中的一部分,其主要作用是决定盒子模型的布局,其子元素将如何定位以及和其他元素的关系和相互作用。那么理解CSS格式化上下文有助于我们掌握各类CSS布局的关键。

行内格式化上下文

行内格式化上下文(Inline Formatting Context),简称IFC。主要用来规则行内级盒子的格式化规则。

IFC 的规则

IFC的行盒的高度是根据包含行内元素中最高的实际高度计算而来。主要会涉及到CSS中的font-sizeline-heightvertical-aligntext-align等属性。

行内元素从包含块顶端水平方向上逐一排列,水平方向上的 marginborderpadding 生效。行内元素在垂直方向上可按照顶部、底部或基线对其。

当几个行内元素不能在一个单独的行盒中水平放置时,他们会被分配给两个或更多的(Vertically-stacked Line Box)垂直栈上的行盒,因此,一个段落是很多行盒的垂直栈。这些行盒不会在垂直方向上被分离(除非在其他地方有特殊规定),并且他们也不重叠。

  • 垂直方向上,当行内元素的高度比行盒要低,那么 vertical-align 属性决定垂直方向上的对齐方式。

  • 水平方向上,当行内元素的总宽度比行盒要小,那么行内元素在水平方向上的分部由 text-align 决定。

  • 水平方向上,当行内元素的总宽度超过了行盒,那么行内元素会被分配到多个行盒中去,如果设置了不可折行等属性,那么行内元素会溢出行盒。

  • 行盒的左右两边都会触碰到包含块,而 float 元素则会被放置在行盒和包含快边缘的中间位置。

IFC 的生成

下面这些规则都会创建一个行内格式化上下文:

  • IFC只有在一个块级元素中仅包含行内级元素时才会生成

  • 内部的盒子会在水平方向,一个接一个的放置

  • 这些盒子垂直方向的起点从包含块盒子的顶部开始

  • 摆放这些盒子的时候,它们在水平方向上的paddingbordermargin所占用的空间都会被考虑在内

  • 在垂直方向上,这些盒子可能会以不同形式来对齐(vertical-align

  • 能把在一行上的盒子都完全包含在一行行盒(Line Box),行盒的宽度是由包含块和存在的浮动来决定

  • IFC中的行盒一般左右边都紧贴其包含块,但是会因浮动元素的存在发生变化。浮动元素会位于IFC与行盒之间,使得行盒宽度缩短

  • 当行内级盒的总宽度小于包含它们的行盒时,其水平渲染规则则由text-align来确定

  • 当行内盒超过行盒的宽度时,它会被分割成多个盒子,这些盒子被分布在多个行盒里。如果一个行内盒不能被分割,则会溢出行盒

IFC 的用途

IFC主要用于

  • 行内元素按照 text-align 进行水平居中

  • 行内元素撑开父元素高度,通过 vertical-align 属性进行垂直居中

块格式化上下文

块格式化上下文(Block Formatting Context,BFC) 是Web页面的可视化CSS渲染的一部分,是块盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域。

BFC实际上就是页面中一块渲染区域,该区域与其他区域隔离开来。容器里面子元素不会影响到外部,外部的元素也不会影响到容器里的子元素。

BFC 内部的盒子会从上至下一个接着一个顺序排列。BFC 内的垂直方向的盒子距离以 margin 属性为准,上下 margin 会叠加。每个元素的左侧最外层边界与包含块 BFC 的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。BFC 的区域不会与浮动元素的盒子折叠。BFC 的高度也会受到浮动元素的影响,浮动元素参与计算。

BFC 的规则

  • 内部的 Box 会在垂直方向上一个接一个的放置

  • 垂直方向上的距离由 margin 决定。(完整的说法是:属于同一个 BFC 的两个相邻 Box 的 margin 会发生重叠(塌陷),与方向无关。)

  • 每个元素的左外边距与包含块的左边界相接触(从左向右),即使浮动元素也是如此。(这说明 BFC 中子元素不会超出他的包含块,而 position 为 absolute 的元素可以超出他的包含块边界)

  • BFC 的区域不会与 float 的元素区域重叠(两栏布局应用)

  • 计算 BFC 的高度时,浮动子元素也参与计算(解决父元素高度塌陷)

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

BFC 的生成

下面这些规则可以创建一个BFC:

注意

display:table 也认为可以生成 BFC,其实这里的主要原因在于 Table 会默认生成一个匿名的 table-cell,正是这个匿名的 table-cell 生成了 BFC

  • 根元素或包含根元素的元素

  • 浮动元素(元素的 float 不是 none

  • 绝对定位元素(元素的 positionabsolutefixed

  • 行内块元素(元素的 displayinline-block

  • 表格单元格(元素的 displaytable-cell,HTML表格单元格默认为该值)

  • 表格标题(元素的 displaytable-caption,HTML表格标题默认为该值)

  • 匿名表格单元格元素(元素的 displaytabletable-rowtable-row-grouptable-header-grouptable-footer-group(分别是HTML tablerowtbodytheadtfoot的默认属性)或 inline-table

  • overflow 值不为 visible 的块元素

  • display 值为 flow-root 的元素

  • contain 值为 layoutcontentstrict 的元素

  • 弹性元素(displayflexinline-flex元素的直接子元素)

  • 网格元素(displaygridinline-grid 元素的直接子元素)

  • 多列容器(元素的 column-countcolumn-width 不为 auto,包括 column-count1

  • column-spanall 的元素始终会创建一个新的BFC,即使该元素没有包裹在一个多列容器中

BFC 的用途

块格式化上下文包含创建它的元素内部的所有内容。其主要使用:

  • 创建独立的渲染环境

  • 防止因浮动导致的高度塌陷

  • 防止上下相邻的外边距折叠

Flex格式化上下文

Flex格式化上下文(Flexbox Formatting Context)俗称FFC。当display取值为flexinline-flex,将会创建一个Flexbox容器。该容器为其内容创建一个新的格式化上下文,即Flex格式化上下文

不过要注意的是,Flexbox容器不是块容器(块级盒子),下列适用于块布局的属性并不适用于Flexbox布局:

  • 多列中的column-*属性不适用于Flexbox容器

  • floatclear属性作用于Flex项目上将无效,也不会把让Flex项目脱离文档流

  • vertical-algin属性作用于Flex项目上将无效

  • ::first-line::first-letter伪元素不适用于Flexbox容器,而且Flexbox容器不为他们的祖先提供第一个格式化的行或第一个字母

Grid格式化上下文

Grid格式化上下文(Grid Formaatting Context),俗称GFC。和FFC有点类似,元素的display值为gridinline-grid时,将会创建一个Grid容器。该完完全全器为其内容创建一个新的格式化上下文,即Grid格式化上下文。这和创建BFC是一样的,只是使用了网格布局而不是块布局。

网格容器不是块容器,因此一些假定为块布局设计的属性并不适用于网格格式化上下文中。特别是:

  • floatclear运用于网格项目将不会生效。但是float属性仍然影响网格完完全全器子元素上display的计算值,因为这发生在确定网格项目之前

  • vertical-align运用于网格项目也将不会生效

  • ::first-line::first-letter伪元素不适用于网格容器,而且网格容器不向它们社先提供第一个格式化行或第一个格式化字母

五、属性值计算过程

什么是属性值计算过程

某一个元素,从最开始的每一个属性都没有值,到每一个CSS属性都全部有值,它到底那些值是怎么来的,它的整个过程是啥,这就是我们的属性值的计算过程

属性值的计算过程分为以下四个步骤, 通过这几个不走可以了解到CSS属性是如何计算出来的

  • 确定声明值
  • 层叠冲突
  • 使用继承
  • 使用默认值

确定声明值

概念

什么叫声明值呢,就是我们样式表里边的CSS属性值

确定声明值的过程就是将作者样式表浏览器默认样式表中没有冲突的声明,作为CSS属性值

例如h1元素,最开始所有的属性都没有值,全是待计算,那么在第一步的时候,就要确定声明值

通过来源将样式表分为两种类型,第一种样式表是作者样式表(开发者的CSS样式表),另一种叫做浏览器的默认样式表(UA样式表)

在这两个样式表中,找到所有那些命中这个h1元素的样式,来确定它的声明值。那这里就会涉及到有属性冲突的问题,作者样式表里边写了一个样式,浏览器的默认样式表里边也写了同样的样式,就必须确定一个优先级,那怎么来确定的呢?

首先找到那些没有冲突的样式,也就是选中的同一个元素中,样式是不一样的属性,冲突的地方,就先不管,只看那些没有冲突的样式,就直接作为它的计算结果。

不过这里需要注意一些细节,在样式计算的过程中,可能会做一些单位上的转换,比方color: red就被转换成rgb(255,0,0)font-size: 2em被转换成font-size: 32px,因为em是一个相对单位,而相对单位就会被转换成绝对单位。font-weight: bold被转换成font-weight: 700,这个bold这个值是一个预设值,它会被转换成一个数字。

属性值的计算过程,也简称为样式计算,而样式计算,它是浏览器渲染过程中的其中一个步骤,浏览器渲染过程总共有七个步骤,其中第二个步骤是样式计算,而它还有很多很多的其他步骤

例如下面的案例中,在未书写任何样式的情况下, 采用浏览器的默认样式。 如果有冲突的声明,那么就需要进行下一步的层叠冲突计算

html
<h1 class="red">hello world</h1>
css
.red {
  color: red;
  font-size: 40px;  /* [!code warning] 冲突值 */
}
h1 {
  font-size: 20px; /* [!code warning] 冲突值 */
}
div h1.red {
  font-size: 3em;  /* [!code warning] 冲突值 */
  font-size: 30px; /* [!code warning] 冲突值 */
}
css
h1 {
  display: block;
  font-size: 2em;  /* [!code warning] 冲突值 */
  font-weight: bold;
}
CSS属性声明值
colorrgb(255,0,0)
text-align待定
font-size待定
font-weight700
displayblock
其他属性待定

层叠冲突

对样式表有冲突的声明使用层叠规则,确定CSS属性值。这个步骤需要计算重要性、特殊性和源次序的判定,具体请参考下文部分六、样式表层叠规则

使用继承值

经过了前面两个步骤之后,仍然有些属性没有值。当属性已经有值就不需要继承,没有属性值就需要考虑该属性是否可以继承。当属性值可以继承,则使用继承,否则进入最后一步

注意

有些属性值可以继承,有些属性是不能自动继承的,像什么背景颜色就不可以继承,一般继承的的属性包含的是文字类的属性,像什么字体大小啊,字体排列方式。

继承将会去检查是否可以继承离自己最近的包含块元素的同属性的值,能继承就使用继承的属性值

可以在MDN Web Docs - CSS/Reference查看到每个属性是否可以继承, 例如font-size就是可以继承的, 而background-color就不可以继承

使用默认值

最终,如果经历上述三个步骤后,该属性值也无法通过继承获得,就会使用浏览器的默认样式

六、样式表层叠规则

CSS中样式是具有层叠规则的, 通过层叠规则来判定样式的优先级, 可用来解决样式冲突, 层叠从上到下的规则为:

重要性

!important总是优先于其他规则, 具有最高优先级, 它会改变层叠正常工作的方式

重要性次序

​作者样式表包含!import > 默认样式表包含!import > 作者样式表 > 默认样式表

特定性

特定性是衡量CSS选择器的具体程度的一种方式, 用于确定应用哪条规则

特定性

特定性计算方式

选择器的特定性是由四位数字[A, B, C, D]构成, 每位数字的值只能取0(否)或者1(是)。开发者可以在IDE中查看对应CSS最终的特定性

第一位A第二位B第三位C第四位D
是否行内样式(内联样式)ID选择器的数量叠加和类选择器伪类选择器 属性选择器的数量叠加和元素选择器伪元素选择器的数量叠加和
  • 比较时,A, B, C, D 四位依次比较,A 较大的权重大,不用看后面。当 A 相同时,比较 B。以此类推。

  • 当 A, B, C, D 相同时,后面的规则会覆盖前面的规则

注意

通用选择器(*)、复合选择器(+ > ~ 空格) 和 否定伪类(:not)在特定性中无影响

源次序

源代码中的书写顺序即为源次序。 如果在样式表中, 重要性和特定性都一致的情况下, 就看在代码中的书写先后顺序, 后写的覆盖前面

七、3D效果技巧

使用CSS3D效果, 可以使元素在三维空间中的动态变换, 为网页带来令人惊叹的交互体验。实现3D效果的规则主要有三条

嵌套HTML

首先准备三层嵌套的html结构

  • 最外层的wrapper-3d用于设置景深
  • 中间一层的container用于设置子元素处于3D平面
  • 最后一层的child用于设置3D的变换
html
<div class="wrapper-3d">
  <div class="container">
    <div class="item child-front">front</div>
    <div class="item child-middle">middle</div>
    <div class="item child-bottom">bottom</div>
  </div>
</div>

设置景深和3D平面

  • .wrapper-3d上设置视角景深perspective
  • .container元素上设置transform-style
css
.wrapper-3d {
  width: 200px;
  height: 200px;
  /* 设置景深, 正值贴近观察者, 负值远离观察者 */
  perspective: 600px;
}

.container {
  width: 100%;
  height: 100%;
  position: relative;
  /* 设置子元素处于3D平面 */
  transform-style: preserve-3d;
}

设置3D变换

给所有3D子元素上设置绝对定位和设置各个方向的3D变换(平移、旋转等等)

css
.item {
  position: absolute;
  width: 200px;
  height: 200px;
}
.child-front {
  transform: translateZ(-100px) rotateY(0deg);
  background-color: lightblue;
}

.child-middle {
  transform: translateZ(-100px) rotateX(-45deg);
  background-color: lightgreen;
}

.child-bottom {
  transform: translateZ(-100px) rotateX(45deg);
  background-color: lightcoral;
}

总的来说, 爷爷元素设置perspective、父亲元素设置transform-style: preserve-3d、子元素们设置绝对定位和3D变换

八、检查溢出元素

在开发过程中,我们经常会遇到元素溢出的情况,这时候我们需要检查元素是否溢出,如果溢出了,我们还需要知道溢出的方向,这样才能更好的解决问题

那么,如何快速找到导致溢出行为的元素呢?

可以使用一个CSS属性outline, 与border所不同的是outline不会影响元素的大小和位置,它只是在元素周围绘制一条线,用于突出显示元素的边框

outline 与 border 的区别

  • 前者不占据空间,绘制于元素内容周围, 而border会占据一定的空间,可能会改变元素的大小和位置
  • 根据规范,outline 通常是矩形,但也可以是非矩形的。
css
/* 可以给所有元素设置一个outline */
* {
  outline: 1px solid red !important;
}

通过添加上述的CSS属性, 就可以非常直观的发现溢出元素包含块的目标元素

检查溢出元素

九、默认属性值

CSS中有几个属性值关键字: initialinheritunsetrevert, 用法和区别如下

  • inherit 用于继承父元素的属性值

  • initial 用于重置属性为W3C规范标准默认值, 就好像没有为该属性编写 CSS 规则一样

  • unset 清除掉浏览器的默认样式, 能使用继承值就使用继承值, 否则重置为W3C规范标准默认值(也就是initial)

  • revert 恢复浏览器的默认样式(user agent stylesheet), 也叫做UA样式表

UA样式表和W3C规范样式表

这两个默认样式表是两个不同的概念

  • 每种浏览器都有一套默认的样式表,在写网页时,没有指定的样式,按浏览器内置的样式表来渲染
  • HTML5 的默认样式(W3C 规范)是指浏览器对 HTML5 元素的默认样式,如h1元素的默认样式是font-size: 2emp元素的默认样式是margin: 1em 0

可以使用all来一次性将所有属性重置

css
div { all: unset }

如需查看常用默认样式表的内容, 请点击以下链接

十、常用概念

Web 页面(文档树)是由很一个个盒子组成(因为任何元素都可以被视为是一个盒子),而视觉格式化模型却是一套规则,用来计算元素转换为盒子的规则。而页面的布局都由这些盒子的所处的各处位置组合而成。那么理解了元素怎么转成盒子的规则,就理解了 Web 页面是怎么布局。

布局因素

而每个盒子的布局主要由以下几个因素决定:

  • 盒子的尺寸:精确指定、由约束条件指定或没有指定

  • 盒子的类型:行内盒子(inline)、行内级盒子(inline-level)、原子行内级盒子(atomic inline-level)和块盒子(block

  • 定位方案:普通流定位、浮动定位或绝对定位

  • 文档树中的其它元素:即当前盒子的子元素或兄弟元素

  • 视窗尺寸与位置

  • 所包含的图片的尺寸

  • 其他的某些外部因素

注意盒子的区别

  • CSS 的盒模型是计算盒子尺寸( width 、 height 、 padding 、 border 和 margin 来决定);
  • 视觉格式化模型是用来计算盒子位置(由前面提到的七个因素来决定)

包含块

什么是块?

块(Block),一个抽象的概念,一个块在文档流上占据一个独立的区域,块与块之间在垂直方向按照顺序依次堆叠(默认情况之下)

包含块(Containing Block),指的是包含其他盒子的块。盒子的定位和大小都是参考一个矩形边缘来计算的,而这个矩形就是元素的容器块

包含块的判断规则如下

盒子的分类

盒子(Box),一个抽象的概念,由 CSS 引擎根据文档中的内容所创建,主要用于文档元素的定位、布局和格式化等用途。盒子与元素并不是一一对应的,有时多个元素会合并一个盒子,有时一个元素会生成多个盒子(比如匿名盒子)

块级盒子(Block-level Box)

概念

块级盒子(Block-level Box),由块级元素生成。一个块级元素至少会生成一个块级盒子,但也有可能生成多个(例如列表项元素)

行内级盒子(Inline-level Box)

概念

行内级盒子(Inline-level Box),由行内级元素生成。同样的,一个行内元素至少会生成一个行内级盒子。行内级盒子包括行内盒子和原子行内级盒子两种,区别在于该盒子是否参与行内格式化上下文的创建

块盒子(Block Box)

概念

块盒子(Block Box),如果一个块级盒子同时也是一个块容器盒子,则称其为块盒子。除具名块盒子之外,还有一类块盒子是匿名的,称为匿名块盒子(Anonymous Block Box),匿名盒子无法被 CSS 选择器选中。

行内盒子(Inline Box)

概念

行内盒子(Inline Box)由非替换元素(Non-replaced Element)生成,属于行内级盒中的一种。除了行内盒子之外,其他的行内级盒子都是原子行内级盒(Atomic Inline-level Box),它的典型特征就是作为一个整体,不能拆分换行。同样的,行内盒子也有匿名行内盒(Anonymous Inline Box)。对于没有任何元素包裹的直接文本,CSS引擎会为其创建匿名行内盒。

原子行内级盒子(Atomic Inline-level Box)

概念

原子行内级盒子(Atomic Inline-level Box),不参与行内格式化上下文创建的行内级盒子。原子行内级盒子一开始叫做原子行内盒子(Atomic Inline Box),后被修正。原子内级盒子的内容不会拆分成多行显示。

行盒(Line Box)

概念

行盒(Line Box)和行内盒是不一样的。行盒是由行内格式化上下文(Inline Formatting Context)产生的盒子,用于表示一行。行盒从包含块的一边排版到另一边。一般情况下,浏览器为会每行创建一个看不见的行盒。

块容器盒子(Block Container Box)

概念

块容器盒子(Block Container Box 或 Block Containning Box),块容器盒子侧重于当前盒子作为容器角色,它不参与当前块的布局和定位,它所描述的仅仅是当前盒子与其后代之间的关系。换句话说,块容器盒子主要用于确定其子元素的之一位、布局等。

元素的分类

在CSS中, 元素可以通过多种方式进行分类, 下面是一些常见的分类方式

I、根据盒子分类

根据“外在盒子”是内联还是块级我们可以把元素分为块级元素和行内级元素之分

块级元素(Block-level Element)

块级元素(Block-level Element)是指元素的display 值为block、list-item、table、flex 和 grid 等,该元素将成为块级元素。元素是否是块级元素仅是元素本身的属性,并不直接用于格式化上下文的创建或布局。

行内级元素(Inline-level Element)

行内级元素(Inline-level Element)是指元素的 display 值为 inline、inline-block、inline-table、inline-flex 和 inline-grid 等,该元素将成为行内级元素。与块元素一样,元素是否是行内级元素仅是元素本身的属性,并不直接用于格式化上下文的创建或布局。

II、根据替换分类

根据是否具有可替换内容,我们也可以把元素分为替换元素和非替换元素

替换元素

替换元素,顾名思义,内容可以被替换。通过修改某个属性值呈现的内容就可以被替换的元素就称为“替换元素”。 以下是替换元素的默认display值

元素ChromeFirefoxIE
<img>inlineinlineinline
<iframe>inlineinlineinline
<video>inlineinlineinline
<select>inline-blockinline-blockinline-block
<input>inline-blockinlineinline-block
<input type="range|file">inline-blockinline-blockinline-block
<input type="hidden">nonenonenone
<button>inline-blockinline-blockinline-block
<textarea>inline-blockinlineinline-block

非替换元素

非替换元素的内容由CSS渲染直接表现给客户端。HTML 的大多数元素是不可替换元素,即其内容直接表现给用户端(例如浏览器)。段落<p>是一个不可替换元素,文字“段落的内容”全被显示。

十一、动画

滑块动画

纯CSS实现滑块拖动动画, 利用animationdelaypaused两个关键属性

源码已经上传到作者的Demos仓库-滑块动画

html
<style>
  .ball {
    --delay: 0s;
    width: 50px;
    height: 50px;
    border-radius: 50%;
    background-color: #f00;
    margin-bottom: 50px;
    animation: move linear forwards 1s paused;
    animation-delay: var(--delay);
  }

  @keyframes move {
    0% {
      transform: translateX(0);
    }

    100% {
      transform: translateX(100px);
    }
  }
</style>

<div class="container">
  <!-- 小球 -->
  <div class="ball"></div>
  <!-- 滑块 -->
  <input type="range" min="0" max="1" step="0.01" />
</div>

<script>
  const input = document.querySelector('input')
  const ball = document.querySelector('.ball')
  const calc = () => {
    ball.style.setProperty('--delay', `-${input.value}s`)
  }
  input.oninput = calc
  calc()
</script>

FLIP动画

所谓FLIP动画, 也就是动画的四个步骤, FirstLastInvertPlay的缩写, 也就是先记录元素的初始状态, 然后记录元素的最终状态, 然后将最终状态反转到初始状态, 最后播放动画, 从而实现动画效果

  • First 记录要监控的元素位置
  • Last 记录元素结构变化后的位置
  • Invert 移动元素到First的位置
  • Play 使用动画还原元素到本来的位置

源码已经上传到作者的Demos仓库-FLIP动画

html
<style>
  ul {
    padding: 20px;
    border: 1px solid #a12d02;
    width: fit-content;
  }

  li {
    list-style: none;
    height: 30px;
    width: 200px;
    border-radius: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: aquamarine;
    margin: 10px 0;
  }
</style>

<button class="first-begin">第一个元素播放FLIP</button>
<button class="full-sort-begin">全体元素排序播放FLIP</button>

<ul class="list">
  <li class="list-item" style="background:#e75723; border-color:#a12d02" order="7">7. HTML + CSS</li>
  <li class="list-item" order="6">6. Javascript</li>
  <li class="list-item" order="1">1. 网络</li>
  <li class="list-item" order="3">3. 工程化</li>
  <li class="list-item" order="5">5. 框架</li>
  <li class="list-item" order="4">4. 移动端</li>
  <li class="list-item" order="2">2. Nodejs</li>
</ul>

<script>
  // 公共元素和函数
  const list = document.querySelector('.list')

  function getLocation(elem) {
    const rect = elem.getBoundingClientRect()
    return rect.top
  }

  function raf(cb) {
    requestAnimationFrame(cb)
  }
</script>

<script>
  // 处理第一个元素的FLIP动画
  ~function handleFirstFLIP() {
    const btn = document.querySelector('.first-begin')
    const firstItem = document.querySelector('.list-item:first-child');
    const lastItem = document.querySelector('.list-item:last-child');
    const start = getLocation(firstItem)
    console.log('first', start);
    btn.onclick = () => {
      // 该行操作并不会立即绘制到浏览器, 而是等待下一个事件循环进行绘制
      list.insertBefore(firstItem, null)
      const end = getLocation(firstItem)
      console.log('end', end);

      const distance = start - end
      firstItem.style.transform = `translateY(${distance}px)`
      console.log('invert', distance)

      raf(() => {
        firstItem.style.transition = `transform 1s`
        firstItem.style.removeProperty('transform')
        console.log('play')
      })

    }
  }()

</script>

<script>
  // 实现全体元素的FLIP排序动画
  const beginBtn = document.querySelector('.full-sort-begin')
  beginBtn.onclick = () => {

    const map = Array.from(list.children).reduce((o, c, n) => {
      const index = c.getAttribute('order')
      o[index] = {
        order: index,
        el: c,
        start: getLocation(c),
        end: -1,
        dis: 0
      }
      return o
    }, {})

    const orderList = Object.values(map).sort((a, b) => b.order - a.order)
    let prev = null
    orderList.forEach(item => {
      list.insertBefore(item.el, prev)
      prev = item.el
      item.end = getLocation(item.el)
      item.dis = item.start - item.end
      item.el.style.transform = `translateY(${item.dis}px)`

      raf(() => {
        item.el.style.transition = `transform 1s`
        item.el.style.removeProperty('transform')
      })
    })
  }
</script>

十二、案例

该部分记录一些CSS有意思的小案例

自动高度

有时候, 我们想在包含块的元素内部, 做一个自动高度的分界线。它的高度随着包含块的变化而自动调整,我们很容易就写出以下代码

最后发现, 作为分界线的子元素内部没有内容撑开, 导致它的实际高度为0

为了能够让该分界线能随着包含块元素撑开, 此时需要给该子元素添加min-height: inherit

css
.parent {
  width: 80%;
  min-height: 100px;
  background: rgb(128, 128, 128, 0.3);
  display: flex;
}

.left,
.right {
  height: 100%;
  width: 45%;
  padding: 4px;
  flex: 1;
}

.border {
  width: 2px;
  height: 100%; 
  min-height: inherit;  
  background: red;
}
html
<link rel="stylesheet" href="./style.css">
<div class="parent">
  <div class="left">
    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Enim facere iure placeat tempora
    natus quasi cumque doloremque, maxime libero porro inventore vel distinctio earum commodi sunt veniam debitis,
    doloribus labore!
  </div>

  <!-- 边界线, 无内容 -->
  <div class="border"></div>

  <div class="left">
    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Enim facere iure placeat tempora
    natus quasi cumque doloremque, maxime libero porro inventore vel distinctio earum commodi sunt veniam debitis,
    doloribus labore!
  </div>
</div>

包含块的高度

  • 包含块有paddingborder, 包含块的高度将是从子元素的上外边距到下外边距(margin)

  • 包含块无paddingborder, 且子元素有边框, 包含块的高度取子元素的上边框到下边框的距离(border), 不包含外边距(margin)

  • 包含块无paddingborder, 且子元素无边框, 则取上下内边距之间的距离, 不会包含子元素的外边距(margin)

具体代码, 请查看作者的demos-包含块的默认高度

十三、一些特殊属性

transform

CSS中的transform用于旋转,缩放,倾斜或平移给定元素等变形

但值得注意的是, 并非所有的盒子都可以进行transform的转换(通常行内级元素不能进行形变)。所以,transform 对行内级非替换元素是无效的,比如spanatable中的一些元素等

多重变形的顺序

transform的多重变形是有顺序的, 如果有多个变形属性, 是从右到左的顺序进行变形的

transform-origin

transform-origin属性让你更改一个元素变形的原点, 默认值是50% 50% 0, 也就是元素自身的中心点为原点

其中属性值可以使用一个,两个或三个值来指定,其中每个值都表示一个偏移量

三种情况的属性值取值类型有点细微区别, 一对中括号代表为一组值, | 表示或

单属性值

css
/* 省略了垂直方向的值, 实际为 left center */
transform-origin: left
/* 省略了水平方向的值, 实际为 center top, 也可以是 top center, 这个顺序不是特别重要 */
transform-origin: top

取值列表: [ left | center | right | top | bottom | <length> | <percentage> ]
  • 一个属性值表示2D变形,省略的值为默认值(center)不变

两个属性值

css
transform-origin: left top

水平取值: [ left | center | right | <length> | <percentage> ]
垂直取值: [ top | center | bottom | <length> | <percentage> ]
  • 给两个属性值也表示2D变形, 其中一个必须是<length><percentage>,或left, center, right关键字中的一个。 另一个必须是<length><percentage>,或top, center, bottom关键字中的一个

三个属性值

css
transform-origin: left top 20px

水平取值: [ left | center | right | <length> | <percentage> ]
垂直取值: [ top | center | bottom | <length> | <percentage> ]
Z轴方向: [ <length> ]
  • 给三个属性值表示3D变形, 前两个值和只有两个值时的用法相同。 第三个值必须是<length>,它始终代表 Z 轴偏移量

mix-blend-mode

mix-blend-mode 属性描述了元素的内容应该与元素的直系父元素的内容和元素的背景如何混合

混合算法

r
Co = 255 - (255 - C1)(255 - C2) / 255

- Co: 混合后的颜色
- C1: 待混合的第一个颜色
- C2: 待混合的第二个颜色

如果C1是白色, 值就是255, 算出最终的混合色为255, 仍然为白色

如果C1是黑色, 值就是0, 算出最终的混合色为C2, 仍然为原色

writing-mode

CSS属性directionwriting-mode、以及text-orientation都可以影响页面的布局方向

语法

css
writing-mode: horizontal-tb;
writing-mode: vertical-rl;
writing-mode: vertical-lr;
  • 默认值horizontal-tb表示,文本流是水平方向(horizontal)的,元素是从上往下(tb:top-bottom)堆叠的
  • vertical-rl表示文本是垂直方向(vertical)展示,然后阅读的顺序是从右往左(rl:right-left),跟古诗的阅读顺序一致 = vertical-lr表示文本是垂直方向(vertical)展示,然后阅读的顺序还是默认的从左往右(lr:left-right),也就是仅仅是水平变垂直

white-space

CSS属性是用来控制空白字符的显示的,同时还能控制是否自动换行

语法

css
white-space: normal | nowrap | pre | pre-wrap | pre-line
  • normal 换行符都被忽略, 连续的空白符会被合并, 但能够自动换行
  • nowrap 换行符都被忽略, 连续的空白符会被合并, 无法自动换行
  • pre 即preserve的缩写, 空格和换行符全都保留, 但无法自动换行
  • pre-line 即preserve+new line+wrap, 换行符保留, 空格被合并, 能够自动换行
  • pre-wrap 也即preserve+wrap, 保留空格和换行符,且可以自动换行
换行符空格和制表符文本换行行末空格行末的其他空白分隔符
normal合并合并换行移除
nowrap合并合并不换行移除
pre保留保留不换行保留
pre-wrap保留保留换行挂起
pre-line保留合并换行移除
break-spaces保留保留换行换行

word-break

这个属性是控制单词如何被拆分换行的

语法

css
word-break: normal | break-all | keep-all
  • normal 使用默认的断行规则
  • keep-all 所有连续的CJK(Chinese/Japanese/Korean)文本一律不换行, 只有空格可以触发自动换行
  • break-all 所有单词碰到边界一律拆分换行

overflow-wrap

该属性在标准化之前叫做word-wrap, 也是用于换行, 主要作为word-break的补充方案。

应用于行级元素,用来设置浏览器是否应该在一个本来不能断开的字符串中插入换行符,以防止文本溢出其行向盒

语法

css
overflow-wrap: normal | break-word | anywhere
  • normal 行只能在正常的单词断点(例如两个单词之间的空格)处换行
  • break-word 如果行中没有其他可接受的断点,则允许在任意点将通常不可断的单词换行(只有当一个单词一整行都显示不下时,才会拆分换行该单词)
  • anywhere 如果行中没有其他可接受的断点,则不可断的字符串可能会在任何时候换行

box-decoration-break

该属性用来定义当元素跨多行、多列或多页时,元素的片段应如何呈现。

语法

css
box-decoration-break: slice | clone
  • slice 元素被按照盒子被切割前的原始样式渲染,之后,针对每个行/列/页面将此假设框渲染成片段

  • clone 每个框片段与指定的边框、填充和边距独立呈现。border-radiusborder-imagebox-shadow独立地应用于每个片段,每个片段的背景也是独立绘制的

clip-path

该属性使用裁剪方式创建元素的可显示区域。区域内的部分显示,区域外的隐藏。它可以按照 svg 路径、盒子模型、基本多边形路径等几种不同的方式来裁切

语法

css
clip-path: <basic-shape> | <clip-source> | <geometry-box>
  • basic-shape 基本图形,包括 inset()、circle()、ellipse()、polygon()

  • clip-source 通过 url() 方法引用一段 SVG 的 <clipPath> 来作为剪裁路径

  • geometry-box 单独使用时,将指定框的边缘作为剪裁路径,或者配合 basic-shape 使用,用于定义剪裁的参考框

使用示例可以参考掘金的一些文章

mask

该属性 mask 允许使用者通过遮罩或者裁切特定区域的图片的方式来隐藏一个元素的部分或者全部可见区域。

mask 属性和 clip-path 的主要区别在于前者用于图像和渐变,而后者用于路径

语法

css
mask: <mask-image> | <mask-mode> |
      <mask-repeat> | <mask-position> |
      <mask-clip> | <mask-origin> |
      <mask-size> | <mask-composite>
  • mask-image 使用的图片资源,默认 none。
  • mask-mode 根据资源的类型自动采用合适的遮罩模式,默认 match-source(目前仅Firefox 支持)
  • mask-repeat 类似于 background-repeat 属性,默认 repeat。
  • mask-position 和 background-position 支持的属性值和表现一样,默认 center。
  • mask-clip 和 background-clip 类似,但是多了 SVG 元素支持,默认 border-box。
  • mask-origin 和 background-origin 类似,但是多了 SVG 元素支持,默认 border-box。
  • mask-size 和 background-size 类似,默认 auto。
  • mask-composite 使用多个图片进行遮罩时候的混合方式,默认 add,表示多个图片遮罩效果累加。

flex-basis

该属性 flex-basis 指定了 flex 元素在主轴方向上的初始大小, 它的默认属性值是auto

语法

css
flex-basis: content | <width>

/* 参数 <width> 的取值范围: */
<width>: auto | <length-percentage [0,]> |
         min-content | max-content |
         fit-content(<length-percentage [0,]>) |
         <calc-size()> | <anchor-size()>
  • flex-basis 优先级高于 width 或 height,但低于 min-width 和 max-width。
  • 设置 flex-basis 可以显式指定元素的初始尺寸,覆盖由内容或 width/height 定义的值。
  • 默认值 auto 会回退到 width 或 height 的值,或由内容决定尺寸。

在 CSS 的 Flexbox 布局中,flex-basis 和元素的自身宽度(或高度,取决于主轴方向)之间的关系可以总结如下:

flex-basis 定义的是主轴方向上的初始大小

  • 如果主轴是横向(默认情况下,flex-direction: row),flex-basis 表示元素的宽度。
  • 如果主轴是纵向(flex-direction: column),flex-basis 表示元素的高度。

flex-basis 的优先级高于元素自身的宽度/高度

  • 如果为元素同时设置了 flex-basis 和 width/height,flex-basis 会覆盖 width 或 height 的值,用作元素的主轴初始尺寸。
css
.item {
  flex-basis: 200px;
  width: 100px;
}
  • 在 flex-direction: row 的情况下,flex-basis 的值(200px)会生效,元素宽度会是 200px,而不是 100px。

flex-basis 的值是 auto

  • 元素的初始尺寸由 width 或 height 决定(取决于主轴方向)。
  • 如果没有明确指定 width 或 height,则根据内容大小或其他布局规则计算。
css
.item {
  flex-basis: auto;
  width: 150px;
}
  • 在 flex-direction: row 时,元素的宽度是 150px,因为 flex-basis 为 auto,会退回去使用 width 的值。

flex-basis 和 flex-grow/flex-shrink 的关系

  • flex-basis 决定元素的初始大小后,flex-growflex-shrink 控制元素如何在容器中扩展或收缩。
  • 如果容器中还有剩余空间或不足空间,Flexbox 会根据 flex-growflex-shrink 的值调整元素的最终大小。
  • flex 元素仅在默认宽度之和大于容器的时候才会发生收缩,其收缩的大小是依据 flex-shrink 的值。
  • flex 容器中分配剩余空间的相对比例是依据 flex-grow 的值。
  • flex 混合属性的全写形式是flex: <flex-grow> <flex-shrink> <flex-basis>, 默认值是0 1 auto

shape-outside

该属性用来定义文字环绕效果, 可以是非矩形的形状,相邻的内联内容应围绕该形状进行包装。默认情况下,内联内容包围其边距框。

语法

css
/* 关键字值 */
shape-outside: none;
shape-outside: margin-box;
shape-outside: content-box;
shape-outside: border-box;
shape-outside: padding-box;

/* 函数值 */
shape-outside: circle();
shape-outside: ellipse();
shape-outside: inset(10px 10px 10px 10px);
shape-outside: polygon(10px 10px, 20px 20px, 30px 30px);

/* <url> 值 */
shape-outside: url(image.png);

/* 渐变值 */
shape-outside: linear-gradient(45deg, rgba(255, 255, 255, 0) 150px, red 150px);

/* 全局值 */
shape-outside: initial;
shape-outside: inherit;
shape-outside: unset;

margin

Margin居中

此处只记录该属性值为auto时, 用来居中的一些条件

  • 块级元素:必须设置明确的宽度

  • Flexbox 布局:父容器设置为 display: flex

  • Grid 布局:父容器设置为 display: grid

  • 绝对定位元素:结合 left: 0right: 0top: 0bottom: 0

具体相关DEMO请查看margin居中进行测试

Margin负值

margin-topmargin-left负值, 元素向上、向左移动

margin-right负值, 右侧元素左移, 自身不受影响

margin-bottom负值, 下方元素上移, 自身不受影响

margin在弹性子元素项中auto值表示用margin去占用剩余空间

background

background 是一种 CSS 复合属性写法,用于一次性集中定义各种背景属性,包括修改 color, image, position, size, attachment, origin, clip 方式等等

语法

css
background: <background-color>
            <background-image>
            <background-position> / <background-size>
            <background-repeat>
            <background-attachment>
            <background-origin>
            <background-clip>

background 复合属性中,各个子属性的顺序很重要。background-size 必须紧跟在 background-position 之后,并用斜杠 / 分隔, 正确的 background 复合属性写法如下:

  • background-color
  • background-image
  • background-position / background-size
  • background-repeat
  • background-attachment
  • background-origin
  • background-clip

参考资料