写点什么

CSS 层叠上下文和层叠顺序原理分析及实战

作者:王丹丹

  • 2020-06-28
  • 本文字数:4082 字

    阅读完需:约 13 分钟

CSS层叠上下文和层叠顺序原理分析及实战

1. 问题现象

最近在做一个网站的首页,首页的右上角有一个绝对定位的大动画,同时首页还有三个小动画,小动画部分的效果是:鼠标滑入特定区域(如下图),展示动画。


由于 UI 提供的动画画布很大,所以遮盖住了下图所示特定区域的 div 元素;最外层 div 元素,包括三个部分:头部标题、设置了透明度的文本以及一段文本。


在 div 元素上设置 onmouse 事件后,诡异的事情发生了:只有鼠标滑入设置透明度的文本时,小动画才出现,滑入其他部分,小动画不出现。


这是为什么?



探究原因,是因为大动画部分设置了绝对定位以及 z-index,形成了层叠上下文,层叠水平高于普通元素,遮盖了 div 中的部分元素。


要解决上述问题,咱们需要先翻开书本,从层叠上下文和层叠顺序说起。

2. 层叠相关概念和原理

2.1 层叠上下文

它是一个三维的概念,在 CSS2.1 规范中,每个盒模型都可看做一个三维空间,分别为平铺在画布上的 X 轴、Y 轴以及表示层叠的 Z 轴。一般情况下,元素在页面上沿 X 轴 Y 轴平铺,用户察觉不到它们在 Z 轴上的层叠关系。而一旦元素发生堆叠,这时就能发现某个元素可能覆盖了另一个元素或者被另一个元素覆盖。


如果一个元素含有层叠上下文,那么这个元素则为层叠上下文元素。我们可以理解为这个元素比普通元素在 Z 轴上距离用户更近一些。


层叠上下文规则:


  • 层叠上下文的层叠水平比普通元素要高;

  • 层叠上下文可以嵌套,内部层叠上下文及其子元素的层叠水平受制于外部的层叠上下文;

  • 层叠上下文和其兄弟元素相互独立,即在不同的层叠上下文中,元素的层叠顺序没有可比性;

  • 不同的层叠上下文元素发生层叠的时候,元素的层叠水平由父级层叠上下文的层叠顺序决定;

2.2 层叠等级

在同一个层叠上下文中,它描述定义的是该层叠上下文中的层叠上下文元素在 Z 轴上的上下顺序。


当然,在普通元素中,也可以有层叠顺序,这时它描述定义的是这些普通元素在 Z 轴上的上下顺序。z-index 属性可以影响层叠水平,注意是“影响”,不是“决定”。

2.3 层叠顺序

以上的层叠上下文和层叠等级是概念,层叠顺序是指元素遵循的规则。在同一个层叠上下文中,其中的元素遵循下图的规则:



其中,border/background 为装饰属性,float 浮动盒子和 block 块状元素一般用作布局,inline/inline-block 内联元素包含的是网页内容。在网页中内容最重要,所以 inline/inline-block 的层叠等级顺序更高。


通过实例验证上述规则,结论如下:


1)负 index 的层叠等级大于层叠上下文、小于 block 块级水平盒子


可参考下文中"一些新的 css 属性会形成层叠上下文"中的第一个例子;


2)float 浮动盒子的层叠等级大于 block 水平盒子



<div style="background: green;width: 100px;height: 100px;float: left"></div><div style="margin-left: -30px;width: 400px;height: 50px;background: red">111111111111</div>
复制代码


3)inline/inline-block 水平盒子的层叠等级大于 float 浮动盒子



<div style="background: green;width: 100px;height: 100px;float: left"></div><span style="margin-left: -30px;background: red">111111111111</span>
复制代码


4)不依赖于 z-index 的层叠上下文的层叠等级大于行内元素的层叠等级(注:此处由于 opacity 属性,形成层叠上下文)



<span style="background: green;display:inline-block;width: 100px;height: 100px;opacity:0.8;vertical-align: middle;"></span> <span style="margin-left:-30px; background:red;vertical-align: middle;">111111111111</span> 
复制代码


5)正 index 的层叠等级大于不依赖于 z-index 的层叠上下文的层叠等级



<span style="background: green;display:inline-block;width: 100px;height: 100px;opacity:0.8;vertical-align: middle;"></span> <span style="margin-left:-30px; background:red;vertical-align: middle;position: relative;z-index: 4">111111111111</span>
复制代码

2.4 层叠准则

  • 在同一个层叠上下文中,如果元素具有明显的层叠水平标识时(如 z-index),层叠水平高的元素在上面,距离用户“最近”;

  • 当两个元素的层叠水平一致、层叠顺序相同时,DOM 流后面的元素会覆盖前面的元素

2.5 层叠上下文的形成

1)页面根元素,本身就具有层叠上下文,称为根层叠上下文


2)定位元素的 z-index 为数值时会形成层叠上下文


  • z-index:auto 时



<span style="background: green;display:inline-block;width: 100px;height: 100px;opacity:0.8;vertical-align: middle;"></span> <span style="margin-left:-30px; background:red;vertical-align: middle;position: relative;z-index: 4">111111111111</span>
复制代码


z-index:auto 时未形成层叠上下文,绿色长方形的 z-index 较大,所以绿色长方形遮盖了红色长方形;


  • z-index 的值为数值时



<div style="position: relative;z-index: 2;">    <div style="background-color: red;width: 100px;height: 200px;position: absolute;z-index: 1">
</div></div><div style="position: relative;z-index: 1;"> <div style="background-color: green;width: 200px;height: 100px;position: absolute;z-index: 2">
</div></div>
复制代码


z-index 的值为数值,形成了层叠上下文,绿色和红色长方形的父元素的层叠等级相比,红色长方形父元素的层叠等级更高,所以红色长方形遮盖了绿色长方形。


以上印证了:不同的层叠上下文元素发生层叠时,元素的层叠水平由父级层叠上下文的层叠顺序决定,同时也印证了 z-index 属性可以影响层叠水平,只是影响,不能决定。


3)一些新的 css 属性会形成层叠上下文


  • 元素为 flex 项,z-index 为数值


z-index 为数值的 flex 项,要满足两个条件,父级元素为 flex 或 inline-flex 布局,子元素的 z-index 为数组,方可形成层叠上下文。


情况 1:当父级元素不为 flex 或 inline-flex 布局时



<div>    <div style="background-color: red;width: 100px;height: 200px;z-index: 1">        <div style="background-color: green;width: 200px;height: 100px;position: relative;z-index: -1">        </div>    </div></div>
复制代码


在同一个层叠上下文中,负 index 的层叠顺序小于 block 块级元素,所以,红色长方形覆盖在绿色长方形的上面;


情况 2:当父级元素为 flex 或 inline-flex 布局时



<div style="display: flex;">    <div style="background-color: red;width: 100px;height: 200px;z-index: 1">        <div style="background-color: green;width: 200px;height: 100px;position: relative;z-index: -1">        </div>    </div></div>
复制代码


当最外层父级 div 元素 display:flex 时,第二层 div 元素 z-index:1(数值),则第二层 div 元素为层叠上下文,由下图的层叠顺序规则可知,负 index 的层叠顺序大于层叠上下文,所以绿色长方形覆盖在红色长方形的上面。



  • opacity


当 opacity 值不为 1 时,可形成层叠上下文


情况 1:当外层 div 元素未加透明度 opacity 时



<div style="background-color: red;width: 100px;height: 200px">    <div style="background-color: green;width: 200px;height: 100px;position: relative;z-index: -1">    </div></div>
复制代码


在同一个层叠上下文中,负 index 的层叠顺序小于 block 块级元素,所以,红色长方形覆盖在绿色长方形的上面;


情况 2:当外层 div 元素加了透明度 opacity 时



<div style="background-color: red;width: 100px;height: 200px;opacity: 0.5">    <div style="background-color: green;width: 200px;height: 100px;position: relative;z-index: -1">    </div></div>
复制代码


当最外层父级 div 元素 opacity:0.5 时,子元素的 z-index:-1,由层叠顺序规则可知,负 index 的层叠顺序大于层叠上下文,所以绿色长方形覆盖在红色长方形的上面;


  • 其他属性


transform 不是 none、mix-blend-mode 不是 normal、filter 不是 none、isolation:isolate 和-webkit-overflow-scrolling:touch,都可形成层叠上下文,此时与 opacity 值不为 1 的情况相同。

3. 解决办法

针对文章开篇提到的问题,有三种解决方案:


方案一:给外层 div 元素设置 position:relative,使 div 元素和绝对定位大动画的层叠水平相等


元素一旦成为定位元素,其 z-index 就会自动生效,且默认值为 auto,也就是 0 级别。由于绝对定位的大动画的 z-index 为 0,也就意味着层叠上下文的大动画元素和 div 定位元素是一个层叠顺序的。


于是当他们发生层叠时,遵循层叠准则第二条,div 元素可正常响应鼠标划入事件。


方案二:通过设置外层 div 元素 position:relative,z-index 为非 auto,形成层叠上下文


当 z-index 的值大于等于绝对定位的大动画的 z-index 值时,当他们发生层叠时,遵循层叠准则第一条,div 元素可正常响应鼠标划入事件。


方案三:通过设置父元素 display: flex|inline-flex,子元素作为 flex 项,z-index 值是不为 auto 的数值时,子元素可形成层叠上下文


可给外层 div 元素设置 diaplay:flex 属性,内层元素设置 z-index 不为 auto,则内层元素为层叠上下文元素,遵循层叠准则第二条,div 元素可正常响应鼠标划入事件。


以上方案是基于绝对定位大动画的 z-index 为 0 时。若绝对定位大动画的 z-index 值大于 0 时,方案一就不生效了,方案二和方案三的 z-index 需大于等于大动画的 z-index 值。

4. 总结

一旦普通元素具有了层叠上下文,其层叠顺序就会变高。它的层叠顺序依赖以下两个原则:


  • 如果层叠上下文元素不依赖 z-index 数值,则其层叠顺序 z-index:auto 可看成 z-index:0 级别;

  • 如果层叠上下文元素依赖 z-index 数值,则其层叠顺序由 z-index 值决定


当遇到页面中有元素发生重叠,或者元素不能正常响应鼠标等事件时,可检查下该元素以及其父元素和兄弟元素所在的层叠上下文。遵循文中所说的规则,设置对应的 css 属性,改变元素层叠顺序,即可解决相应的问题。


本文转载自公众号贝壳产品技术(ID:beikeTC)。


原文链接


https://mp.weixin.qq.com/s?__biz=MzIyMTg0OTExOQ==&mid=2247485712&idx=2&sn=d788cf74f5d806636d54b6fd30b6025f&chksm=e8373a60df40b3763973faddf3d4db114591a07b3c4158ee1f939aefcab057e5afb939ada720&scene=27#wechat_redirect


2020-06-28 14:073367

评论

发布
暂无评论
发现更多内容

分享:在阿里做Java开发的这五年,收获与感悟

Java架构师迁哥

OpenYurt v0.4.0 新特性发布:高效地管理边缘存储资源

阿里巴巴云原生

云原生

速度,力度,广度:金融数智化中的华为“寻道”

脑极体

反洗钱监管再度升级,看这家金融集团如何应对

索信达控股

大数据 银行 金融监管 风险管理 数据管理

如何设置HashMap初始化大小

Hex

后端 hashmap

网络攻防学习笔记 Day34

穿过生命散发芬芳

网络攻防 6月日更

百度搜索与推荐引擎的云原生改造

百度开发者中心

云原生

腾讯云携手信通院启动“云原生开源白皮书”编写,深度解读云原生

CODING DevOps

腾讯云 DevOps 云原生

区块链在数据管理中有哪些价值?

CECBC

“openEuler未来发展” 采访熊伟博士

容光

操作系统

23种设计模式,正确的解读方式原来是这样

Java架构师迁哥

系统性思维 系统之美1

张老蔫

28天写作

系统性思维 系统之美2

张老蔫

28天写作

新大陆!阿里P9整理出:Java架构师“成长笔记”共计23版块

Java架构师迁哥

Tapdata 数据库实时同步的技术要点

tapdata

数据库迁移 数据同步 实时数据分析

区块链:可持续发展的世界的有效工具?

CECBC

Redis--哈希冲突

是老郭啊

redis hash

《开源 PassJava》1、项目介绍

悟空聊架构

开源 面试 刷题 spring cloud alibaba 6月日更

defi流动性挖矿系统开发(案例版)丨defi流动性挖矿源码现成版

系统开发咨询1357O98O718

持续测试 | 测试流程提效:在 CODING 中实践迭代内的持续测试

CODING DevOps

DevOps 测试计划 持续测试 迭代式测试

资产信息化、数字化和通证化—— 理解区块链世界新经济的优势

CECBC

python——使用input()函数

在即

6月日更

🐬【MySQL技术导航】带你认识一下数据库的锁

码界西柚

MySQL MySQL锁 6月日更

研发自动化,你准备好了么?

PingCode研发中心

研发管理 研发效能 研发工具 研发团队

WebSocket 对象简介

编程三昧

大前端 websocket

权限与认证:JWT

程序员架构进阶

Token JWT 认证授权 28天写作 6月日更

ARTS--week 10 补打卡

steve_lee

龙蜥专场精彩回放来了!10位技术大咖、242位开发者相聚

阿里云基础软件团队

《原则》(三)

Changing Lin

百度开发者中心全新升级 | 文末六一送福利

百度开发者中心

百度 福利

MySQL 基础之一:基本命令使用

打工人!

MySQL 6月日更

CSS层叠上下文和层叠顺序原理分析及实战_大前端_InfoQ精选文章