细读 CSS | 层叠上下文
在 HTML 页面中 我们通常会使用 margin、float、offset 等 CSS 属性控制元素在 X 轴和 Y 轴页面中的位置 另外会使用 z-index 和 transform 来控制元素在 Z 轴的排列顺序。
因此 我们可以得知 HTML 页面其实是一个三维的结构。
了解以下这几个词
层叠上下文是一个比较抽象的概念。当一个元素拥有了一个“三维”的表现 即在 Z 轴上有一定的顺序 那么我们称该元素有一个层叠上下文。
层叠等级 Stacking Level有了层叠上下文之后 需要一个叫做层叠等级 层叠水平 的家伙来决定同一个层叠上下文中元素在 Z 轴上的显示顺序。
层叠顺序 Stacking Order层叠顺序是一种用于描述层叠等级的特定规则。
如何区分
层叠上下文和层叠等级是概念 而层叠顺序是规则。通俗地讲 前两者就像是公司和领导 负责画饼和制定计划 而后者则是打工人 负责具体实施 就是搬砖 。
本文中所提到的“层叠元素”和“定位元素”均按以下描述进行约定 有特殊说明除外。
层叠元素 指含有层叠上下文的元素 主要用于区别不含层叠上下文的普通元素 。定位元素 指含有 position 属性 且其值不为 static 的元素。层叠元素有以下特性
层叠元素的层叠等级要比普通元素高。层叠上下文可以阻断元素的混合模式。层叠上下文可嵌套 内部层叠上下文及其所有子元素均受限于外部的层叠上下文。每个层叠上下文和兄弟元素独立 也就是当进行层叠变化或渲染的时候 只需要考虑后代元素即可。每个层叠上下文是自成体系的 当元素发生层叠的时候 整个元素都被认为是在父层叠上下文的层叠顺序中。以下几种方式 都会创建层叠上下文
页面的根元素 html 自带一个层叠上下文。我们知道 绝对定位元素通过 top/right/bottom/left 来定位时 若没有其他定位元素限制 会相对浏览器窗口定位。其中缘由 就是因为有根层叠上下文的原因。
定位元素层叠上下文 传统
当元素的 position 不为 static z-index 不为 auto 时 该元素就会创建层叠上下文 但有些特例 下文会提到 。
CSS3层叠上下文 新时代 在 CSS3 中新增了很多新属性 有一些会创建层叠上下文。z-index 值不为 auto 的 flex 项 父元素 display:flex|inline-flex 元素的 opacity 值不为 1 元素的 transform 值不为 none 元素 mix-blend-mode 值不为 normal 元素的 filter 值不为 none 元素的 isolation 值为 isolate will-change 指定的属性值为上面任意一个 元素的 -webkit-overflow-scrolling 设为 touch。
特例说明
在远古神兽 IE5/6/7 浏览器下 z-index: auto 会创建层叠上下文。至于这是 Bug 还是微软故意为之 就不去考究了 反正也接触不到这些远古神兽了。在现代浏览器 包括 IE8 及以上 中 z-index: auto 都不会创建层叠上下文。在过去 position: relative | absolute | fixed 都需要配合 z-index 数值 才会创建新的层叠上下文。但是不知道什么时候起 Chrome、Firefox、Safari、Edge 等浏览器 position: fixed | sticky 元素天然层叠上下文 无需 z-index 为数值。 2022.01 亲测一旦普通元素具有了层叠上下文 其层叠顺序就会变高。
分为两种情况
如果层叠元素不依赖 z-index 那么其层叠顺序看成与 z-index: auto 一致的 z-index: 0 级别。如果层叠元素依赖 z-index 那么其层叠顺序由 z-index 决定。*张鑫旭大佬原图出处 请看这里。
Q 为什么定位元素会层叠在普通元素的上面
当一个元素成为了定位元素 z-index 会自动生效 取其默认值 auto。根据层叠顺序图 我们可以看到 z-index: auto 的层叠顺序比 block、float、inline/inline-block 都要高。
当元素发生层叠时 其覆盖关系遵循以下两个准则
谁大谁上 在同一层叠上下文中 层叠等级较大的会覆盖较小的。后来居上 在同一层叠上下文中 若层叠等级 顺序 相同时 在 DOM 文档流中处于后面的元素会覆盖前面的元素。请注意 上面两条准则的前提都是“处于同一层叠上下文中” 那是因为在不同层叠上下文的比较是没有意义的。
接下来 介绍一些常见的层叠上下文、层叠顺序的案例 帮助进一步理解。
它们取值如下
position: static(default) | relative | absolute | fixed | sticky
z-index: auto(default) | integer | inherit
当 position 不为 static 时 z-index 才会生效。
其中 z-index 属性值如下
*其实还有一些全局值 如 initial、unset 但不重要 故忽略。
请注意 在 CSS3 里当父元素设为 display: flex | inline-flex 子元素使用 z-index 时 也会使得子元素创建新的层叠上下文 请注意 创建新的层叠上下文是子元素 而不是父元素哦 。
换句话说 z-index 不再是定位元素独享 它还可以与 flex 搞在一起做点什么。
基础示例一 源码及演示
基础示例二 源码及演示
以上两个示例都很基础 不多说了...
从结果上看 可以知道
层叠顺序 z-index: -1 z-index: auto | z-index: 0 z-index: 1 且 z-index: auto 和 z-index: 0 层叠顺序均为 0 级别。当 z-index: auto 和 z-index: 0 的定位元素同时存在 发生层叠时 在 DOM 流中处于后面的元素会覆盖前面的元素 同一层叠上下文中 。请记住 处于同一层叠上下文中 遵循谁大谁上、后来居上的准则。
图中示例的区别在于 inner 盒子外层的 outer 盒子 一个设为 z-index: auto 另一个设为 z-index: 0。
假设 z-index: auto 会使得元素本身创建新的层叠上下文 那么 inner1 和 inner2 对应的参照物分别是 outer1 和 outer2 此时 inner1 中的 z-index 值 其实是没有意义的 无论它的值是正数、负数、还是 0 或 auto 它总会显示在 outer1 上面 inner2 同理 。同时 由于 outer1 和 outer2 的层叠顺序是一致的 outer2 在 DOM 流后面 属于“后来居上” 因此 outer2 会覆盖 outer1。
倘若假设成立 蓝色块应覆盖在绿色块上面 效果应如示例二相同 可事实并非如此。因此 我们可以得出结论 z-index: auto 并不会创建新的层叠上下文。
给你们看看远古神兽 IE5/6/7 浏览器下 z-index: auto 创建层叠上下文的效果。
Internet Explorer 7
因此 较为严谨的说法是 在现代浏览器 包括 IE8 及以上浏览器 中 z-index: auto 不会创建层叠上下文。
究其原因 其实很简单 前面也提到过。比较层叠顺序时 应在同一层叠上文中进行对比 否则是无意义的。
在示例一 red 盒子和 green 盒子处于同一层叠上下文 即根层叠上下文 中 且 z-index: -1 层叠顺序较低 所以可以看到 red 盒子覆盖了 green 盒子。
在示例二 由于 red 盒子的 z-index: 0 是使得其本身创建了新的层叠上下文 所以 green 永远不会穿越 red 盒子。此时 即使 green 盒子的 z-index 为 -9999 也总会显示在 red 盒子上层。
在示例三中 red 盒子内多了一个 blue 盒子 并设为 z-index: 1。由于 blue 盒子和 green 盒子同属于 red 盒子创建的层叠上下文 因而 此时 z-index 就有了比较的意义了。所以 我们可以看到 green 盒子处于 blue 盒子下方 且不会穿过 red 盒子。
再看另外一个示例 源码及演示
它的原因是一样的 因为除了 z-index 会创建层叠上下文之外 CSS3 中的 transform 也会创建新的层叠上下文哦
以上两个都是常见的 z-index “失效”的场景 根本原因还是 有的小伙伴不知道 transform 也会创建层叠上下文。
当遇到 z-index 失效时 Check List 如下
检查当前元素是否为定位元素 或其父级元素是否有 display: flex | inline-flex 检查它们是否处于同一层叠上下文 若前面条件都确认无疑 那说明有一些你不知道的 CSS 属性会创建层叠上下文 此时应去翻阅文档 否则 就是这垃圾浏览器没有遵循标准 偷偷地创建了新的层叠上下文 这种情况应该几乎不会出现 毕竟微软都放弃 IE 转投 Chromium 了 。于 2022.01 亲测 Chrome 97、Safari 15.2、Firefox 97、Microsoft Edge 97 浏览器下面 表现均如上图。由于 position: fixed | sticky 自带层叠上下文 因此图中示例三、示例四蓝色块会覆盖绿色块。
在 CSS3 中有几个与动画相关的属性 transform、transition、animation 分别对应变换、过渡、动画。虽意义相近 但具体角色不一。它们的取值如下
.selector { transform: transform-function | none; transition: property duration timing-function delay; animation: name duration timing-function delay iteration-count direction fill-mode play-state; }
本文重点并非谈谈它们的区别 我们接着看 transform。与之相关的 CSS 属性有这些
.selector { /* 设置元素变形方式 如旋转、缩放、倾斜、平移等 */ transform: transform-function | none; /* 更改一个元素变形的原点 */ transform-origin: length | percentage | center | left | right | top | bottom; /* 定义与 transform、transform-origin 这两个属性有关联的布局框 */ transform-box: content-box | border-box | fill-box | stroke-box | view-box; /* 设置元素的子元素是位于平面中 还是 3D 空间中 */ transform-style: flat | preserve-3d; }
它还有兼容性属性
.selector { -webkit-transform: transform-function | none; -moz-transform: transform-function | none; -ms-transform: transform-function | none; -o-transform: transform-function | none; transform: transform-function | none; }
其中 transform-function 的变换函数有以下这些
透视 perspective() 查看示例 矩阵 matrix()、matrix3d() 查看示例 倾斜 skew()、skewX()、skewY() 查看示例 缩放 scale()、scaleX()、scaleY()、scaleZ()、scale3d() 查看示例 旋转 rotate()、rotateX()、rotateY()、rotateZ()、rotate3d() 查看示例 平移 translate()、translateX()、translateY()、translateZ()、translate3d() 查看示例我们知道 只要 transform 不为 none 时 它也会创建层叠上下文。还有 translateZ()、perspective() 两个变换函数 可以实现 z-index 类似的效果。translateZ(tz) 就是 translate3d(0, 0, tz) 简写形式。
从字面上看 我们可以很容易知道 translateZ 的变换是属于 3D 空间的。
先看个简单示例
为什么红色块的 translateZ 值更大 但却不是在更上面呢
原因很简单 就是因为 transform-style 的默认值是 flat 即处于 2D 平面中。如果选择平面 元素的子元素将不会有 3D 的遮挡关系。
它的取值有
transform-style: flat | preserve-3d
只要将其添加一个 transform-style: preserve-3d 的父元素 就能得到预期结果 红色覆盖在绿色上面。preserve-3d 使得该元素的子元素应位于 3D 空间中。
再看示例 源码及演示
以上示例多组对比 其实还是为了说明 在不同层叠上下文中比较层叠高低是没有意义的。
看示例 源码及演示
我们来看下 iPhone 微信浏览器下 表现如何
微信浏览器 iOS 版
看到差异了吗
原因是 苹果旗下的 Safari 浏览器在使用 3D 变换时 会忽略 z-index 的作用。
详情戳这里 Safari 3D transform 变换 z-index 层级渲染异常的研究
个人建议
未完待续...
使用z-index,必须要知道CSS层叠上下文! 目前前端大环境下,很多人越来越不注重学习 CSS 了,觉得太简单!但是事实并非如此,CSS 的复杂程度其实一点不亚于 JavaScript,只是你没深入了解才会觉得简单。 不知道你有没有遇到过这样的布局问题:我明明设置了 z-index,怎么没起作用?我设置了元素定位,最终效果怎么和我想象得不一样? 如果你遇到了上面得问题,说明你对 CSS 了解得还不够深入。上面问题的出现,都与 CSS 层叠上下文相关,所以今天我们决定彻底将 CSS 层叠上下文搞明白!
相关文章
- 如何只用CSS完成漂亮的加载
- CSS盒模型
- CSS中.和#区别
- CSS position
- 怎样使用自定义标签简化 js、css 引入?
- CSS中4种常用选择器
- HTML和CSS学习
- CSS – PostCSS
- CSS – rem, em, px
- ****CSS各种居中方法
- Hover.css:一组超实用的 CSS3 悬停效果和动画
- v-bind绑定style时css属性名的写法(驼峰和短横线)
- css基础(精英版)
- html/css 滚动到元素位置,显示加载动画
- CSS文字样式
- css:详解BFC块级格式化上下文
- 【Debug】 你所知道的Elements - CSS----第三天
- 你所不知道的CSS滤镜技巧与细节
- 14.div+css实战三 阿里云src响应中心主体内容上
- CSS直接实现的响应式