Web布局:CSS定位和层叠控制

发布于 大漠

在这一章节中我和大家将一起来探讨CSS中的另一个知识重点,即CSS的定位(position)和层叠控制(z-index。这两个属性都隶属于**CSS Positioned Layout Module Level 3**模块。当人们在接触布局的时候都比较倾向于使用定位的方式。因为定位的概念看起来易于掌握。使用定位来布局,表面上你确切的指定了一个元素所处的位置,它坐落在那里。可事实上,定位要比你看到要稍微复杂一点。对于很多CSS的新手,CSS定位和层叠控制还是有些东西会被绊倒,所以在它们成为你惯用技巧前你需要先掌握它们。

普通流和CSS盒模型

首先需要更正一个概念:

W3C规范中并没有文档流(Document Flow)这一个概念,大家平时所说的文档流指是就是W3C规范中的普通流(Normal Flow)

如果我们想要更好的掌握CSS定位和层叠控制,那就需要先对普通流和盒模型有一定的认识。但在这篇文章中我们不会花很多时间来阐述这两方面的知识,因为我们在以前的章节中有过这方面的介绍。

在任何一个Web页面或Web应用程序都会有HTML这样的一个模板,在没有任何CSS或JavaScript控制的情况之下,模板会按客户端(浏览器)默认渲染进行排列。而这样的一个排列仅依赖于HTML源码出现顺序来排列。

从直观上理解正常流指的是元素按照其在 HTML 中的位置顺序决定排布的过程,主要的形式是自上而下,一行接一行,每一行从左至右:

形象一点说,流就好比我们生活中的水流,在不同的器皿中将会有不同的形状。这是因为不同的盒子使用的是不同的格式化上下文(Formatting Context)来布局,每个格式化上下文都拥有自己不同的渲染规则,而这些规则是用来决定其子元素如何定位,以及和其他元素的关系。好比水倒进不同的器皿中,会有不同的形态:

上图中的各种器皿如果用到CSS中来,那这些器皿就是对应的CSS盒子。也就是我们常说的盒模型。盒子有不同的类型,不同类型的盒子的格式方法也有所不同,不同的盒子也会影响元素或其后代元素的行为。在CSS中,通常使用display属性来明确盒子的类型。

CSS的display切换不同的值时,会产生生成不同的盒子;同时客户端渲染HTML文档时都会为每个元素自带一个盒子。CSS中的每一个盒子都会具有一个内容区、内容区被一个内边距包裹,内边距外是盒子的边框,并且在边框之外会一个外边距用于与其他盒子分隔开。如果用一张图来描述的话,它可能是像下面这样:

如果将CSS逻辑属性引入进来之后,我们将来描述一个盒模型,应该用下图来阐述,更为准确:

而在CSS定位中有另一个模型,常被称为定位模型,其规定了一个盒子在总体的布局上应该处于什么位置以及对周围的盒子会有什么影响。定位模型包括了常规文档流,浮动和几种类型的定位元素。

对常规文档流和盒模型有了一个基本的认识之后,我们就可以开始一起来聊聊CSS的定位和层叠控制了。

CSS的定位position

CSS的position属性即是我们常说的定位,主要用来指定一个元素在文档中的定位方式。元素显式设置了定位属性(除static)之外的值都需要依赖于CSS的TRBL规则的设置,才能最终决定该元素的位置。

CSS中的TRBL规则

CSS的**TRBL**实际上是四个CSS属性的简称,即toprightbottomleft

如果将其运用到Web坐标系统中的话,大致如下图这样:

上面是一种老的技术,如果换到逻辑属性中来,那么:

  • top » offset-block-start
  • rgiht » offset-inline-end
  • bottom » offset-block-end
  • left » offset-inline-start

不过上面列出的值不是唯一模式,因为它的结果和书写模式writting-mode有着紧密的联系:

改变writing-mode的值有着不同的结果:

而CSS的position属性要让元素定位生效又离不开这几个属性之一。

定位类型

CSS的position属性的值主要有:

position: static | relative | absolute | sticky | fixed

默认情况下,元素的position的值是static,也是页面中的普通流。另外四种定位是relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和sticky(粘性定位)。

  • static:指定元素使用正常的布局行为,即元素在文档常规流中当前的布局位置。此时 top, right, bottom, leftz-index 属性无效
  • relative:元素先放置在未添加定位时的位置,再在不改变页面布局的前提下调整元素位置(因此会在此元素未添加定位时所在位置留下空白)。position:relativetable-*-group, table-*等元素无效
  • absolute:不为元素预留空间,通过指定元素相对于最近的非 static 定位祖先元素的偏移,来确定元素位置。绝对定位的元素可以设置外边距(margin),且不会与其他边距合并
  • fixed:不为元素预留空间,而是通过指定元素相对于屏幕视口的位置来指定元素位置。元素的位置在屏幕滚动时不会改变。fixed 属性会创建新的层叠上下文。当元素祖先的 transform 属性非 none 时,容器由视口改为该祖先
  • sticky: 盒位置根据正常流计算(这称为正常流动中的位置),然后相对于该元素在流中的 Flow root(BFC)和 Containing block(最近的块级祖先元素)定位。在所有情况下(即便被定位元素为 table 时),该元素定位均不对后续元素造成影响。position: stickytable 元素的效果与 position: relative 相同

Web页面上绝大多数元素是根据页面的普通流(static)进行布局。只有当你想要做更高级的事情时,比如调整元素到特定位置,或者在不打乱周围元素的情况下使用UI组件具有动画效果,那么非static都可以发挥作用。

可以想象,当一个元素的position属性的值不是static时,该元素被称为定位元素

接下来我们看看非static的定位的使用。

相对定位

相对定位的元素会根据TRBL来决定自己的位置。但只是相对于元素自身原来所处的位置进行移动。在某种意义说,相对定位元素移动之后的结果和使用margin移动元素有点相似,但也有一个重要区别。区别就是围绕在相对定位元素附近的元素会忽略相对定位元素的移动,即不会影响相邻的元素。

相对定位元素设置topleft属性分别从原始框的顶部和左侧边缘开始计算;设置bottomright属性相对于其他边进行偏移:

绝对定位

绝对定位的元素会脱离文档流。对于包围它的元素而言,会视绝对定位元素不存在,就好比设置了display: none一样。如果你想要保持它所占有的位置而不被其他元素所填充,那么你就需要使用其他的定位方式。

绝对定位和相对定位有所不同。如果绝对定位元素的祖先元素没有显式的设置为定位元素的话,那么绝对定位元素会相对于浏览器视窗进行定位。

如果绝对定位元素的祖先元素显式的设置为定位元素的话,那么绝对定位元素会相对于离其最近的定位元素进行定位:

绝对定位中的toprightbottomleft让元素偏移的方式和相对定位是类似的。

固定定位

固定定位和绝对定位非常类似,固定定位的元素也会脱离文档流;不同的是之固定定位的元素不会相对定位元素进行定位,而是相对于浏览器视窗进行定位。固定定位还有一个特别之处,那就是固定元素不会随着页面的其余部分滚动。

很多时候,希望固定元素不是相对于视窗定位,而是相对于某个容器进行定位。这个时候需要使用到CSS Containment相关的特性。该特性有许多选项,可以限制浏览器的样式、布局和对特定元素的绘制。这在修改DOM时特别有用。在浏览器中,哪怕是很小的变化都有可能会造成浏览器重绘制整个页面,这样的消费是很昂贵的,即使浏览器努力为我们做了很多优化,页面的重绘还是对性能有一定影响的。

使用CSS Containment,我们可以把整个页面圈起来,然后说“这里发生了什么,只在这里做相应的事”。这也是另一种方法,可以保护元素不受外部的变化而受影响。

CSS的Containment中的contain取值为paint时,就可以让元素作为一个包含绝对定位和固定定位后代的块。这意味着,固定元素的祖先元素要是显式设置了contain:paint的话,固定定位就会相对于该元素进行定位,不再相对于视窗进行定位。另外,在CSS中还可以使用transform: translateZ(0)获得相同的效果。

CSS Containment是较新的特性,目前只在Chrome 52+版本可以看到。如果你想深入的了解这方面的知识,可以阅读《CSS Containment in Chrome 52》一文。

粘性定位

粘性定位(sticky)元素和其他的定位元素略有不同。staticrelativeabsolutefixed之间的主要区别是它们在DOM流中所占的空间。staticrelative元素位置在文档流中保持它们的自然空间,而absolutefixed元素会脱离文档流,并且具有浮动行为。

粘性定位有点类似于相对定位(relative),但也有点类似于固定定位(fixed)。粘性定位类似于相对定位,但是其偏移量是参照最近的具有滚动框的祖先计算,如果没有具有滚动框的祖先元素,则最后会参照浏览的视窗进行计算。

或许有不少同学都和我一样地认为,当滚动到一定位置时,粘性定位元素就会粘在视窗指定位置不动。比如下面这个示例:

.sticky {
    position: sticky;
    top: 80px;
}

当页面滚动时,.sticky元素距离视窗顶部80px时,该元素就会粘着不动,其行为有点类似于fixed定位。此时看到的是正常的效果。但问题是,有时它不会起作用,比如说滚动其他部分,它就会停止粘住。如果用到实例中来解释的话,当一个具有position:sticky的粘性定位元素被包装时,它是包装器元素中唯一的元素,也就是这个元素被定义为粘性定位元素。此时粘性定位元素不会有效果。

<!-- 不能正常工作 -->
<style>
    .sticky{
        position: sticky;
        top: 0;
    }
</style>
<div class="wrapper">
    <div class="sticky">
            SOME CONTENT
    </div>
</div>

当在粘性定位元素的容器中塞入更多元素时,它就能正常工作。这样做的原因是,当一个元素被设置为粘性定位元素时,粘性定位元素容器是唯一一个可以粘住该元素的区域。该元素没有任何可以浮动的元素,因为它只能浮动于同级元素之上。

粘性定位元素主要包含两个部分:粘性定位元素粘性定位元素容器

  • 粘性定位元素:指的就是显式设置了position:sticky的元素。当视窗位置匹配了定义的值时,粘性定位元素将脱离文档流
  • 粘性定位元素容器:指的是包裹粘性定位元素的容器,这是粘性定位元素可以浮动的最大区域

来看一个简单示例:

CSS层叠控制

这里所说的层叠控制是指如何通过CSS的z-index来控制元素在z轴的顺序。理解或者掌握层叠的控制我们有几个重要的概念需要理解。

三维空间

平时我们从设备终端看到的HTML文档都是一个平面的,事实上HTML文档中的元素却是存在于三个维度中。除了大家熟悉的平面画布中的x轴和y轴,还有控制第三维度的z轴。

其中x轴通常用来表示水平位置,y轴来表示垂直位置,z轴表示屏幕内外方向上的位置。

对于xy轴我们很易于理解,一个向右,一个向下。但对于z轴,理解起来就较为费力。在CSS中要确定沿着z轴排列元素,表示的是用户与屏幕的这条看不见的垂直线:

从正常流的一节中我们可以知道,如果元素不脱离文档流,或者不通过其他CSS的规则来改变初始化的格式化上下文环境,元素盒子是不可能会有层叠在一起的。但我们使用floatposition:absolute时可以让元素脱离文档流。那么问题来了:

  • 当一个设置了z-index值的定位元素与常规文档流中的元素相互重叠的时候,谁会被置于上方?
  • 当定位元素与浮动元素相互重叠的时候,谁会被置于上方?
  • 当定位元素被嵌套在其他定位元素中时会发生什么?

要回答这些问题,我们需要进一步地理解z-index是如何工作的,尤其是层叠上下文,以及层叠次序这些概念。

层叠上下文

上一节提到过,网页及其每个元素都有一个坐标系统。该系统包括一个三维z轴,其中的元素是**层叠(Stacked)**的。z轴的方向指向查看者,x轴指向屏幕的右边,y轴指向屏幕的底部。

通常,浏览器会按照CSS规范中指定的特定顺序放置元素:

在DOM树中最先出现的元素被放在首位,之后出现的元素被放在前面的元素之上。但它并不总是那么简单。只有当页面上的所有元素是自然流才起作用。也就是说,当没有元素在流中的位置被改变或者已经脱离文档流,才起作用。

CSS中有两种方式影响元素的流和位置的方法:

  • 使用position属性定位元素。除了默认的static值外的元素被称为定位元素
  • 通过使用float属性浮动元素来改变元素的流

事实上,每个HTML元素都属于一个层叠上下文。给定层叠上下文中的每个定位元素都具有一个整数的层叠层级,具有更大堆栈级别的元素盒子总是在具有较低堆栈级别的盒子的前面(上面)。盒子可能具有负层叠级别。层叠上下文中具有相同堆栈级别的框根据文档树出现的顺序层叠在一起。

文档中的层叠上下文由满足以下任意一个条件的元素形成:

  • 根元素 (HTML)
  • z-index 值不为 auto 的 绝对/相对定位
  • position 值为 fixedsticky
  • 一个 z-index 值不为 auto 的 Flex 项目 (Flex item),即:父元素 display: flex|inline-flex
  • opacity 属性值小于 1 的元素
  • transform 属性值不为 none的元素
  • mix-blend-mode 属性值不为 normal 的元素
  • filterperspectiveclip-pathmaskmask-imagemask-bordermotion-path 值不为 none 的元素
  • perspective 值不为 none 的元素
  • isolation 属性被设置为 isolate 的元素
  • will-change 中指定了任意 CSS 属性,即便你没有直接指定这些属性的值
  • -webkit-overflow-scrolling 属性被设置 touch 的元素

而且每个网页都有一个默认的层叠上下文。这个层叠上下文的根源就是html元素。html元素中的一切都被置于这个默认的层叠上下文的一个层叠层上。理解起来有点怪。那么先来看一个图:

层叠上下文1 (Stacking Context 1)是由文档根元素形成的。 层叠上下文2和3 (Stacking Context 2, 3) 都是层叠上下文1 (Stacking Context 1) 上的层叠层。 他们各自也都形成了新的层叠上下文,其中包含着新的层叠层。

这样是不是有点理解,如果没有,也不要紧,先把这个概念放下,先来理解另外两个概念:层叠水平和层叠顺序

叠层水平

@张鑫旭 老湿在《深入理解CSS中的层叠上下文和层叠顺序》一文中把叠层水平描述的非常表象又易于理解。我就直接做为搬运工,把这部分内容搬过来占为己用了。

层叠水平(Stacking Level)决定了同一个层叠上下文中元素在z轴上的显示顺序。Level这个词很容易让我们联想到我们真正世界中的三六九等、论资排辈。在真实世界中,每个人都是独立的个体,包括双胞胎,有差异就有区分。例如,又胞胎虽然长得很像,但实际上,出生的时间还是有先后顺序的,先出生的那个就大(大哥或大姐)。网页中的元素也是如此,页面中的每个元素都是独立的个体,他们一定是会有一个类似排名排序的情况存在。而这个排名排序、论资排辈就是我们这里所说的层叠水平。层叠上下文元素的层叠水平可以理解为官员的职级,一品两品,县长省长之类;对于普通元素,这个嘛...你自己随意理解。

于是,显而易见,所有的元素都有层叠水平,包括层叠上下文元素,层叠上下文元素的层叠水平可以理解为官员的职级,一品两品,县长省长之类。然后,对于普通元素的层叠水平,我们的探讨仅仅局限在当前层叠上下文元素中。为什么呢?因为否则没有意义。

这么理解吧~ 上面提过元素具有层叠上下文好比当官,大家都知道的,这当官的家里都有丫鬟啊保镖啊管家啊什么的。所谓打狗看主人,A官员家里的管家和B官员家里的管家做PK实际上是没有意义的,因为他们牛不牛逼完全由他们的主子决定的。一人得道鸡犬升天,你说这和珅家里的管家和七侠镇娄知县县令家里的管家有可比性吗?李总理的秘书是不是分分钟灭了你村支部书记的秘书(如果有)。

翻译成术语就是:

普通元素的层叠水平优先由层叠上下文决定,因此,层叠水平的比较只有在当前层叠上下文元素中才有意义。

需要注意的是,诸位千万不要把层叠水平和CSS的z-index属性混为一谈。没错,某些情况下z-index确实可以影响层叠水平,但是,只限于定位元素以及Flex盒子的孩子元素;而层叠水平所有的元素都存在。

层叠顺序

在HTML文档中,默认情况之下有一个自然层叠顺序(Natural Stacing Order),即元素在z轴上的顺序。它是由许多因素决定的。比如下面这个列表,它显示了元素盒子放入层叠顺序上下文的顺序,从层叠的底部开始,共有七种层叠等级:

  • 背景和边框:形成层叠上下文的元素的背景和边框。 层叠上下文中的最低等级
  • z-index:层叠上下文内有着负z-index值的子元素
  • 块级盒:文档流中非行内非定位子元素
  • 浮动盒:非定位浮动元素
  • 行内盒:文档流中行内级别非定位子元素
  • z-index: 0:定位元素。 这些元素形成了新的层叠上下文
  • z-index:定位元素。 层叠上下文中的最高等级

这七个层叠等级构成了层叠次序的规则。 在层叠等级七上的元素会比在等级一至六上的元素显示地更上方(更靠近观察者)。 可以结合w3help中的一张图来帮助我们更好的理解这七个层叠等级:

其实对于层叠顺序规则还是较为复杂的。

当页面包含浮动元素、绝对定位的元素、固定定位的元素或相对定位的元素(元素从正常位置偏移一定量)以及内联元素时,浏览器会以不同的方式显示它们(放置它们)。元素从最靠近查看者的地方排列到最远的地方,如下所示:

  • 定位元素按源代码中的外观顺序排列。源代码中的最新内容最接近查看者
  • 内联元素(比如文本和图像)是流入和非定位(它们的位置是静态的)
  • 非浮动元素按照源代码中外观的顺序排列
  • 非定位和非浮动块级元素
  • 根元素html是全局层叠上下文的根,包含页面上的所有元素

这就是浏览器在呈现页面上的元素时应用的默认层叠顺序。

如果你想要更改定位元素在z轴上的渲染顺序,可以使用z-index属性。例如,你有两个绝对定位的元素,它们在某个点上重叠,并且你希望其中一个元素显示在另一个元素的前面,即使它在源代码中出现在它之前,你也可以使用z-index属性来实现这一点。

此时需要注意的第一件重要的事情是,z-index属性只适用于定位元素。所以,即使为元素提供z-index的值将其置于其他元素之前,z-index也不会对元素产生影响,除非它被定位;也就是说,除非它具有除static之外的position值。

因此,如果所有定位的元素具有z-index的索引值,则将元素从最靠近查看者排列到最远的位置,如下所示:

  • 具有正值的z-index的定位元素。较高的值更接近屏幕。然后,按照它们出现在源代码中的顺序排列
  • 定位元素的z-index:0z-index: auto
  • 内联元素(如文本和图像)是流中的和非定位的(它们的位置是静态的)
  • 源代码中出现顺序的非定位浮动元素
  • 非定位和非浮动块级元素
  • 具有负值的z-index的定位元素。较低的z-index索引值更近。然后按照它们在源代码中出现的顺序
  • 根元素html是全局层叠上下文的根,包含页面上的所有元素

当我们在定位元素上设置z-index值时,它指定该元素在它所属的层叠顺序上下文中的顺序,并且它将根据上述步骤在屏幕上渲染。

但是,当我们设置元素的z-index时会发生另一件事。获取除默认值auto之外的z-index值的元素实际上为其所有定位的后代元素创建层叠上下文。我们之前提到过,每个层叠上下文都有一个根元素,它包含其中的所有元素。当你将z-index属性应用于这个元素时,它将在其包含的下下文中指定元素的z轴顺序,并且还将创建以该元素为根的新层叠顺序上下文。

一个具有值为z-index:auto的定位元素被视为创建了新的堆叠顺序上下文,但任何实际创建新层叠顺序上下文的定位后代和后代被视为父层叠顺序上下文的一部分,而不是新的层叠顺序上下文。

当一个元素成为一个新的层叠顺序上下文时,它所定位的后代元素将会按照我们前面提到的元素本身的规则在其中进行层叠渲染。因此,如果我们再次重写渲染过程,它会是这样的:

  • 具有正值z-index的定位元素组成的层叠顺序上下文。较高的值更接近屏幕。然后按照它们在源代码中出现的顺序呈现
  • 定位元素的z-index: 0z-index: auto
  • 内联元素(比如文本和图像)是流中的和非定位的(它们的位置是静态的)
  • 非浮动元素按照源代码中外观的顺序排列
  • 非定位和非浮动块级元素
  • 具有负值z-index的定位元素组成的层叠顺序上下文。较低的z-index的值更接近屏幕。然后按照它们在源代码中出现的顺序呈现
  • 根元素html是全局层叠上下文的根,包含页面上所有元素

因此,当我们使用z-index属性来确定其层叠顺序中定位元素的顺序时,我们还创建了“原子(Atomic)”层叠顺序上下文,其中每个元素成为其所有定位后代的层叠顺序上下文。

可视化理解层叠上下文

你可以将构成层叠的元素视为你小时候可能玩过的积木。这些积木是一堆不同颜色的圆形木块,你可以把它们堆在一起。

现在,想象一下两座塔紧挨在一起,每个塔上都有一堆圆圈,彼此紧挨着。这两个塔类似于页面上的两个定位元素,每个元素都为其后代形成一个层叠上下文。

当两个层叠上下文重叠时,它会变得更复杂(但并不困难)。为了理解层叠上下文重叠时会发生什么,可以想想一个汉堡包三明治。

每个汉堡包都含有堆叠在一起的食物(奶酪、西红柿、洋葱,如果你不是素食者的话,可能是肉)。每个汉堡包代表其内部食物切片的堆叠环境。在第二个旁边的另一个汉堡包也是其内部切片的层叠上下文。现在,想象一下把两个汉堡放在一起。顶部的两个汉堡代表页面上重叠的两个定位元素。通过将两个汉堡包堆叠在一起,你实际上已经给了一个比底部更高的层叠顺序。

你可以想象,上汉堡里的食物片不能比上汉堡里的食物片高 —— 它们被限制在它们的叠加环境中,并且无论它们的z-index指数有多高,都将保持在它的边界内。

下图展示了一个生活中真实的层叠上下文的例子。它由几个重叠的层组成。每一层都是堆放书和其他东西的上下文。从底层第二层的书总是放在上层内容下面。除非对层进行更改或重新定位(经定不同的z轴的值),否则总是如此。

层叠上下文A的内容可以放在另一个层叠上下文B的内容前面的唯一方法是,给A一个大于Bz-index值,当然,它们的前提是有一个相同层叠上下文的环境。

我们要讲的最后一个可视化示例可能描述元素在页面上绘制方式的最好例子之一。

网页实际上就像一幅油画画布。浏览器按照一定的顺序绘制画布上的元素,就像画家在画布上绘制对象一样。从最远处开始,再到最近的。下面的绘制先用Mr.Z来描述:

如果你根据“画家算法”来思考,即物体被画在一个场景上的前后顺序,那么层叠上下文就像一幅画中的一幅画。首先,你要按照正确的顺序把所有的东西都画在后面,然后当你要画它的父上下文时,把整个结果贴在它所属的地方。

层叠算法在每个原子层叠上下文中都与在全局根(html)上下文中一样。

构成层叠上下文的元素的背景和边框总是落在上下文中的所有元素后面。

我们到目前为止所提到的要点可以从CSS规范中归纳为以下几点:

  • 渲染树在画布上绘制的顺序是按照层叠上下文来描述的。层叠上下文可以包含进一步的层叠上下文。从父层叠上下文的角度来看,层叠上下文是原子的;其他层叠上下文中的边框可能不会位置任何边框之间
  • 每个盒子属于一个层叠上下文。在给定的层叠上下文中,每个定位盒子都有一个整数的层叠级别,它在z轴上的位置相对于同一层叠上下文中的其他层叠级别。具有较高层叠级别的盒子总是在具有较低层叠级别的盒子前面格式化。盒子可能有负的层叠级别。在层叠上下文中,具有相同层叠级别的盒子按照文档树顺序从后到前堆叠
  • 根元素形成了层叠上下文。其他层叠上下文是由任何定位元素(包括相对定位的元素)生成的,其计算值为z-index,而不是auto。层叠上下文不一定与包含块相关

元素层叠是如何控制

通过前面的介绍,我们对CSS中的层叠或者说z-index有了一定的了解。但在使用的时候在不同的场景中还是略有差异的。比如:

未使用z-index的层叠控制

在默认情况之下,任何元素的z-index的值都是auto

其表现形式会按照下面这样的规则进行层叠控制:

  • 根元素的背景和框
  • 非定位的后代元素,按照在HTML中的出现顺序进行层叠
  • 定位的后代元素,按照在HTML中的出现的顺序进行层叠

浮动块默认如何堆叠

如果存在浮动块,浮动块的堆叠顺序会介于无定位元素和定位元素之间。即:

  • 根元素的背景和边框
  • 非定位的后代块元素,按照在HTML中的出现顺序进行堆叠
  • 浮动块
  • 定位的后代块元素,按照在HTML中的出现顺序进行堆叠

使用z-index自定义堆叠顺序

使用z-index可以自定义堆叠顺序。z-index的值可以为整数(正数、负数、0均可)。使用方法很简单。

需要注意以下三点:

  • 未指定z-index,默认为auto
  • 如果z-index相同,则按照默认规则比较
  • z-index只能用于定位了的元素

如何在z轴上滚动

CSS的transform属性出现之后,我们有时候场景会在一个3D场景中。那么在一个3D场景中我们是如何在z轴上滚动呢?

在CSS的3D场景指是的transform3D中,允许我们通过perspective或在z轴上的旋转来控制DOM元素。想要在3D空间中渲染DOM元素,我们可以通过以下属性来控制:

  • perspective
  • perspective-origin
  • transform3D相关的属性

其中perspective用来设置用户和屏幕间的距离。其值越小,场景失真就越大:

perspective-origin可以用来决定查看器正在查看的位置:

正如我们在透视图中所看到的一样,translateZ()允许我们沿着3D空间的z轴定位一个元素。

不过,transform 有的时候会让 z-index “临时失效”,其实并非 z-index 失效了,只是 z-index 被用在不同的 Stacking Context 上,而非在默认的 Context 上同等地比较层级了。所以 DOM 在 transform 的工程中,DOM 处于一个新的 Stacking Context 里,z-index 也是相对于这个 Stacking Context 的,所以表现出来的实际是 Stacking Context 的层次,动画一结束,DOM 又回到默认的 Context 里,这时的 z-index 才是在同一个 Context 上的比较。

其解决方案大致会有两种:

  • 方法1:父级,任意父级,非body级别,设置overflow:hidden可恢复和其他浏览器一样的渲染
  • 方法2:以毒攻毒。也可以使用3D transform变换

如何控制z

控制z可以通过z-indextransform来实现的。先简单的了解一下这两种控制z轴的方法。

通过z-index控制z轴,需要配合position属性,且position的属性值为relativeabsolutefixedsticky时。并且给z-index显式的设置数值,数值越大,其层级越高。简单点说,数值越高,元素越在顶上。

transform可以通过它的translateZ()来改变元素的层叠顺序,其值越大,越在顶层,离屏幕越近。不过通过transform:translateZ()改变元素z轴的层级,必须在元素的父元素中显示的设置transform-style: preserve-3d或者在transform中显示的设置perspective()。如下所示:

小结

该文主要介绍了CSS另一个重点知识点,即CSS的定位和层叠控制。简单地说,如何使用CSS的position中的relativeabsolutefixedsticky来对元素进行定位处理。另外,在Web中我们除了xy之外还有另一个轴,那就是z轴,主要用来处理元素在z轴空间的层叠顺序。一般情况之下,CSS的定位元素和z-index会紧密的配合在一起使用。这对于布局有着非常重要的作用。也就是说,如果你彻底地掌握Web的布局,那么CSS的positionz-index相关的知识是不可或缺的。

最后希望这篇文章有助于大家理解和提高这方面的知识。