lightningminers / article

⚡️闪电矿工翻译组-前端(front end developer)
GNU Lesser General Public License v3.0
58 stars 9 forks source link

CSS中z-index的工作原理解析 #32

Open henry-fun opened 5 years ago

henry-fun commented 5 years ago

原文地址: https://blog.logrocket.com/how-css-works-creating-layers-with-z-index-6a20afe1550e

原文作者: Benjamin Johnson

介绍

这篇文章是"How CSS workd"系列文章中的第三篇(深入探讨CSS基本构建模块的系列文章,这些基本构建模块有时会让人感觉像是黑魔法)。无论你如何编写你的css代码,知道样式表的运行原理总是极好的,这样你才能编写出高效的、可伸缩性css代码。

下面是系列文章中的前两篇:

  1. css如何工作 — 解析和绘制css的关键路径:深入研究CSS引擎在浏览器请求css文件到用户实际在屏幕上看到的像素过程中发生了什么
  2. css如何工作 — 了解层叠:讲解“css中的"c"以及层叠算法如何将各种css规则解析为浏览器中实际显示的样式

今天我们研究css的另一个黑魔法 — z-index,耍z-index,通常感觉像是往墙面上扔一个更高的数字,然后看看是什么效果。

但如果我们试着剥离z-index上的层,看看管理他们的规则呢,我们会发现它并不像我们想象的那么可怕。

究竟什么是z-indx?

说到底,我们在浏览器中使用的屏幕是一组由像素组成的二维平面,但如果你经常上网,你可能会经常体会到"三维”世界,例如,你可以单击一个按钮打开一个 modal,或者触发一个tool-tip

如果你假设用户正在看屏幕,并想象屏幕是三维世界的入口,你可以开启浏览器中的三维空间呈现,首先,我们把该空间想象成一个房间,那么"最远"的"墙"就被称为"画布 canvas"

当在屏幕是绘制像素时,浏览器首先会确保绘制距画布最近(或最远)的像素,然后,他将渐进的将元素"更接近"用户,并覆盖以前绘制的像素

z-index属性表示元素在三维浏览器视觉中绘制的顺序,默认情况下,所有元素的 z-index 皆为0,浏览器按照 DOM 顺序进行绘制,然而实际上,z-index 为我们提供了何时绘制元素的细节控制,通过指定较高的 z-index,我们可以使绘制出的元素"更接近"用户,如果我们指定一个较低的(或负值)z-index,我们可以使绘制出的元素更接近画布。

在深入研究 CSS 的位置和布局规范时,我发现了这个有用的图片,它可以直观的显示出 z-index 层叠对用户视觉的影响。

img

具有较高 z-index的元素看起来"更接近"用户

如果我们只看到 z-index 的这种特性,我们很容易被诱导认为它就是 z-index 的全部知识了:更高的 z-index就更接近用户,更小的 z-index 则更远离,然而,z-index 还有一些细节常被混淆。

z-index 的第一个注意点是它必须在被设定了 position 属性元素上时才会生效,这意味着,z-index 只有在position 设置为除 static 属性上的元素上时,它才能更改层叠顺序,在元素没有设置任何 position 的情况下,z-index 将不会起任何作用。

层叠上下文介绍

当我们探讨到 z-index 的第二个注意点时,z-index 变得更加让人困惑了,因为 z-index 只适用于层叠环境中的元素。

层叠上下文涉及到了 HTML 节点和它的所有子节点,HTML 元素位于层叠上下文的 root 级别,它可以被称为根层叠。

文档的默认层叠上下文(或"根层叠上下文")将 HTML 元素标记为其"根层叠",并且默认情况下,所有元素都属于此根层叠上下文,但是,任何元素节点也可以是其"局部层叠上下文"中的根层叠。

你可以通过以下几种方法将元素指定为新的局部层叠上下文的根层叠:

  1. 在设置了positionabsoluterelative或其它任何除了auto属性上设置 z-index
  2. 使用position: fixedposition: sticky
  3. 元素上设置的opacity属性值小于1
  4. 在元素上使用transformwill-change

如果你想看到更多创建层叠环境的方法,请在 MDN上查看该文章

作为例子,让我们想象 HTML的三个树节点,将它们想想为三个层叠,在下图中,每个层叠的底部是HTML节点,子元素堆叠在其上(有点像 HTML 树的颠倒表示),让我们假设这些元素都是 body 标签的直接子节点。

img

如果我们在浏览器中预览这些HTML元素,它将表示为如下:

注意到什么有趣的事情了吗?

首先,为什么blue-child-1低于其它标签?它的z-index是1000000,它不是应该在最上面吗?

此外,为什么green-child-1为什么显示在所有元素的顶部,及时它的z-index为2,我们不是说更高级别的z-index胜过更低级别的z-index吗?

让我们从green-child-1元素开始,因为它的父元素green-parent的z-index为999,我们期望green-parent元素也高于其它元素,但如果我们回想一下使用 z-index的第一个警告:它对静态定位的元素没有影响,这意味着green-child-1是层叠上下文的一部分,它是根据red-parentblue-parent(根层叠中唯一的其它两个元素)进行测量的,它的z-index比这两个更高,所有它显示在顶部。

了解red-parentblue-parent各自创建的局部层叠环境也有助于我们了解为什么blue-child-2显示在red-parent之下,即使其z-index更高,由于z-index仅控制元素在其局部层叠上下文中的位置,因此blue-child-2肯定要高于blue-parent的子级。但是red-parentz-index要高于blue-parent,无论它们的z-index是多少,red-parent内部的所有元素都将比blue-parent元素显示出更高的 z-index

为了使blue-child-2显示在red-parent之上,我们实际上必须要更改HTML结构,使得blue-hild-2脱离它当前的局部层叠上下文环境,并使其进入根层叠上下文(或者至少是位于red-parent之上的层叠上下文)。

在大型web应用中,这样变更通常是很麻烦切危险的,特别是当你试图管理语义化的HTML结构或模块化组件结构时。

你经常看到许多组件库通过将元素添加到 body 标签中,而不是包含在你自己定义的组件中以此来实现更高曾经的UI 组件(如 tool-tips和modal),这么做可以在很大程度上避开这些层叠上限文"陷阱",这么做也可以确保严重依赖层叠的组件的z-index始终处于根层叠上下文环境中,因此它们可以将z-index设为值1000,并相信他将显示在大多数元素的顶部。

结论

在一些情况下,使用 z-index 可能很麻烦,但是当我们知道它的底层原理及工作规则时,我们会发现它的行为实际上是相当可预测的,我想99%的关于z-index的混淆都源自我们在上面讨论的那两个警告的误解

作为复习,我在这里再强调一遍:

  1. z-index仅用于除 static 元素外的具有position的元素
  2. z-index仅适用于元素在其所属的层叠上下文中的位置

我希望你以后在遇到多层 ui 导致的各种陷阱时,了解 z-index的内部结构会给你带来信心,我知道,研究z-index和层叠系统对于我们更好的开发ui会更有帮助。

lazysheep commented 5 years ago

红蓝绿那张图及解释看的真的很混乱