CSS 中的层叠上下文

在使用 bootstrap 的模态框时,出现了半透明遮罩始终盖住模态框的情况,
只有把模态框的 HTML 写到 body 之下才恢复正常,
当时郁闷得对着屏幕举了半分钟的中指。(程序员最好看看佛经,学会清心寡欲)

后来有幸看到了张鑫旭在慕课网 CSS深入理解之 relative 的视频,
才算真正知晓了层叠上下文在 CSS 中的规则和运用技巧。

还有些名称比较类似的学术语言和概念,格式化上下文、执行上下文、音频上下文、2D上下文等,感兴趣地可以去搜一下玩玩。

废话有些多,下面就正式开始吧!

什么是层叠上下文和层叠水平

层叠上下文其实把它理解为 z 轴也没有问题,屏幕上面叠着一层层图层(不是所有的元素都是)。

层叠水平则表示同一层层叠上下文对应的图层。

创建层叠上下文

那么怎样才能产生层叠上下文,让它的 z 轴不一样呢?
以下情况会创建层叠上下文(随着 CSS3 属性还在增加,本表不全):

  • z-index 不为 auto 的 position 定位元素
  • z-index 不为 auto 的 flex
  • opacity 不为 1
  • transform 不为 none
  • perspective 不为 none
  • filter 不为 none
  • mix-blend-mode 不为 normal
  • 带有 isolation: isolate
  • will-change 不为 none
  • 带有 -webkit-overflow-scrolling: touch
<style>
.box {
  width: 100px;
  height: 100px;
  margin: 0 auto -50px;
}
.box1 {background: pink}
.box2 {background: grey}
.box1 {left: 10px}

.box1 { transform: translate(0,0) }
</style>

<div class="box box1"></div> <!-- 粉色 -->
<div class="box box2"></div> <!-- 灰色 -->
20171122221904

此例中 .box1 创建了层叠上下文,z 轴上就比普通元素层级更高了,因此覆盖了 .box2。

同级层叠水平下的层叠顺序

而当有两个层叠上下文时,它们就有了兄弟和父子关系(假设现在都处于同级层叠水平)。

当为兄弟关系时(层叠上下文的兄弟关系,非 DOM 的兄弟),它们总是 后者居上 的。

<style>
.box {
  width: 100px;
  height: 100px;
  margin: 0 auto -50px;
}
.item {
  /* 两个 dom 中子级创建层叠上下文,构成兄弟关系的层叠水平 */
  transform: translate(0,0);
  width: 90px;
  height: 90px;
  top: -10px;
}
.box1 {background: pink}
.box2 {background: grey}
.item1 {background: red}
.item2 {background: green}
.item1 {left: -10px;}
.item2 {left: -20px;}
</style>

<div class="box box1"> <!-- 粉色 -->
  <div class="item item1">item1</div> <!-- 红色 -->
</div>
<div class="box box2"> <!-- 灰色 -->
  <div class="item item2">item2</div> <!-- 绿色 -->
</div>

<!-- .item 变为层叠上下文,所以比 .box 这种普通元素层级更高. -->
<!-- 且 .item 遵循后者居上,所以 .item2 覆盖了 .item1 -->
20171122162647

当为父子关系时,子级就存在父级的“作用域”内了,父级高我才高,不然我再高也没用。

<style>
.box { position: relative; z-index: 0 }
.item1 { z-index: 999; }
/* 紧跟上例,本来 .item2 由谁后谁上的原则位于上面。 */
/* 当现在其父级 .box 也创建的层叠上下文,且遵循谁后谁上,因此 .box2 要高于 .box1。 */
/* 此处 .item1 的父级已经低于 .box2 了,本身层级再高也还是在 .box1 的范围内而已。 */
</style>
20171122164912

注:此处 position: relative; 但不写 z-index: 0(即 z-index: auto),它会覆盖普通元素,但并不会创建层叠上下文。 下面这个也是类似的例子,

.box { position: relative; margin: 0 0 10px }
.item {
  position: relative;
  z-index: -1;
}
.box2 { z-index: 0; }
/ 只有 z-index 不为 auto 才创建层叠上下文 /
20171123134429

不同级层叠水平下的层叠顺序

既 z-index 不为 auto 的情况,这时它们总是 谁大谁上 的。

<style>
.box1 { z-index: 2; }
/* 这个就很好理解了,.box 现在是兄弟关系,.box1 的 z-index 更大,那么它就更高 */
</style>
20171122173308

关于最初 BUG 的解读

回到最初那个 bootstrap 内容框在黑底下的问题,就很方便理解了。

<div class="content">
  <div class="modal"></div> <!-- modal 内容框 -->
</div>
<div class="modal-backdrop"></div> <!-- 黑色半透明底 -->

.modal 和 .modal-backdrop 都定位了,成为了层叠上下文,而如果 .content 由于需要或误操作也创建了层叠上下文,那么 .content 和 .modal 就有了兄弟关系,谁后谁上+谁大谁上,最终造成黑底的层级高于了 .content 的层级,而 .modal 作为 .content 范围内子级也就因此被覆盖住了。

最终,要么我们把 .modal 从 .content 拿出放置于与 .modal-backdrop 同级,要么把 .content 的层级提高得比 .modal-backdrop 更大(如果 .content 还有背景色那就是另一回事了)。

所以个人认为 bootstrap 这样写是不好的,要遇上一个不懂层叠上下文的,想破头也不知道为什么。
我推荐下面的这种写法:(自创的,后来看 layui 等框架也是这样写的,看来我没做错)

<style>
.modal {
  position: absolute;
  top: 0; bottom: 0;
  left: 0; right: 0;
  opacity: 0;
  z-index: -1;
  overflow: hidden;
  transition: opacity .3s, z-index 0s .3s;
}
.modal-bg {
  position: absolute;
  top: 0; bottom: 0;
  left: 0; right: 0;
  opacity: 0;
  transition: opacity .15s;
  background: rgba(0,0,0,.8);
}
.modal.in {
  opacity: 1;
  z-index: 1;
  transition: opacity .3s, z-index 0s;
}
.modal.in .modal-bg {
  opacity: 1;
}
.modal-box {
  transform: translate3d(0,0,0);
}
</style>

<div class="modal">
  <div class="modal-bg bg"></div>  <!-- 黑底 -->
  <div class="modal-box">内容</div>
</div>

就像张鑫旭大神所说,如果你理解了层叠上下文,那么就根本不需要把 z-index 设得很大了,1-2 足矣。

非层叠上下文的覆盖关系

在张大神的 此文 中存在着这样一张图,一直让我产生着奇怪的误解。

zxx

后来才想通,这是非层叠上下文时的覆盖关系,但很容易让我们产生混淆。

比如 float / inline 等,和 relative 下 z-index 非 auto 一样,都是会产生覆盖,但不会创建层叠上下文。

里面的 background 和 z-index 真的实在是蛊惑人心,个人认为大可忽略不论。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,725评论 1 92
  • 1.z-index基础 z-index属性指定了元素及其子元素的[z顺序],而[z顺序]可以决定当元素发生覆盖的时...
    徐国军_plus阅读 6,336评论 1 6
  • 作者:HaoyCn文章源自:https://segmentfault.com/a/1190000003825614...
    IT程序狮阅读 865评论 0 0
  • 今天和大家说一个用金钱征服世界的“第六帝国”,统治欧洲金融长达百年,富过八代的世界第一家族——罗斯柴尔德家族(Ro...
    双棒fengtaisb阅读 1,377评论 5 12
  • 关于那天的失联 我真的有千万的不该 在看你的找到我时湿润红着的眼睛和哭腔的责骂 我从没像那一刻一般恨我自己 没人能...
    最喜梨园阅读 175评论 0 0