关于css:译CSS-定位与层叠上下文Stacking-context

4次阅读

共计 3262 个字符,预计需要花费 9 分钟才能阅读完成。

你是否在应用定位时,会遇到一个定位元素即便设置更高的层级,也无奈将另一个定位元素笼罩的状况?通过了解层叠上下文,你就能更好的构建你的利用。

了解渲染流程和层叠程序

当浏览器将 HTML 解析成 DOM 构造时,它同样会创立另一个树结构 – 渲染树。通过它渲染之后就成了用户所看到的视图,它同样决定了浏览器绘制 HTML 元素的程序。这个程序很重要,因为越是前面渲染的元素,它的显示程序就越靠前,也就可能会笼罩后面的元素。

在没有定位的状况下,这个渲染程序是由 HTML 元素的程序决定的,例如有以下 HTML 构造:

<div>one</div>
<div>two</div>
<div>three</div>

它们的层叠程序如下图,在这里作者应用了一些负的外边距来强制元素笼罩,然而没有应用定位。越是前面渲染的元素,它显示的就越靠前。

当你应用定位时,这种体现就会扭转。浏览器首先绘制没有应用定位的元素,而后绘制应用定位的元素。默认状况下,应用定位的元素会显示在没有应用定位的元素后面,在下图中,将后面的两个元素设置了定位 position: relative,它们的显示程序就会变得靠前,覆盖住第三个没有定位的元素(position 属性的默认值是 static,即没有定位),即便位于 HTML 构造中的程序没有扭转。

留神这些定位元素中,第二个元素依然显示在第一个元素后面。首先定位元素会显示在失常元素后面,而后依据 HTML 构造中的程序来决定定位元素的显示,也就是前面的靠前显示。

什么是层叠上下文

由浏览器绘制出的一个或多个元素组成的 DOM 树造成一个层叠上下文。当你设置 z-index 属性到一个定位元素上(position 为 relative,absolute,fixed,sticky),这个元素就会创立一个新的层叠上下文,并且会成为这个层叠上下文的根元素。这个根元素的所有子元素都是这个层叠上下文的一部分。

层叠上下文的子元素都是在它的外部绘制,所以位于层叠上下文内部的元素无奈设置本身的地位处于层叠上下文外部的子元素之间。也就是说内部元素 C 无奈处于层叠上下文的子元素 A 和 B 的两头。同样,当设置一个元素 C 覆盖住另一个元素 D 时,D 元素外部的子元素是无奈位于元素 C 下面的。

用例子来论述下:

<div class="box one positioned">
 one
 <div class="absolute">nested</div>
</div>
<div class="box two positioned">two</div>
<div class="box three">three</div>

下面 HTML 构造中蕴含三个类名为 box 的元素,其中的两个会应用定位,并且设置它们的 z-index 值为 1。第一个 .box 元素中的 .absolute 子元素会应用定位并且设置 z-index 的值为 100;即便 .abosolute 的的 z-index 值很高,然而它依然会被第二个 .box 元素笼罩,因为它的父元素 – 第一个 .box 元素的层叠上下文位于第二个 .box 元素的层叠上下文的后面。

以下为 CSS 代码

body {margin: 40px;}
.box {
 display: inline-block;
 width: 200px;
 line-height: 200px;
 text-align: center;
 border: 2px solid black;
 background-color: #ea5;
 margin-left: -60px; // 使前面的元素笼罩在其余元素上
 vertical-align: top;
}
.one {margin-left: 0;}
.two {margin-top: 30px;}
.three {margin-top: 60px;}
.positioned {   // 每一个定位元素都会建设一个 z-index 为 1 的层叠上下文
 position: relative; 
 background-color: #5ae; 
 z-index: 1; 
} 
.absolute {
 position: absolute;
 top: 1em;
 right: 1em;
 height: 2em;
 background-color: #fff;
 border: 2px dashed #888;
 z-index: 100;  // z-index 属性仅会管制本身处于以后层叠上下文中的地位
 line-height: initial;
 padding: 1em;
}

因为被第二个 .box 元素笼罩的第一个 .box 元素属于层叠上下文的根元素,所以它的子元素,应用相对定位的 .positioned 元素不会显示在第二个 .box 元素的后面,即便这个子元素领有很高的 z-index 值。

通过给定位元素增加 z-index 属性是最常见的创立层叠上下文的形式,然而还有其余的一些属性同样能够创立层叠上下文。例如:

设置透明度小于 1、transform、filter、属性等都会创立层叠上下文,这些属性会影响到元素和它的子元素,所以它们属性同一个层叠上下文。文档根元素(html)元素同样会为整个页面创立一个顶级的层叠上下文。

当然还有其余属性也会创立层叠上下文,但咱们用的还是比拟少,感兴趣的能够参考 MDN

一个层叠上下文中的元素依照以下程序显示,从前面到后面:

  • 层叠上下文的根元素
  • 定位元素具备负的 z-index 值的(它子元素也是一样的)
  • 未定位元素(position: static 是默认值,相当于没有定位)
  • 定位元素具备 z-index: auto(它的子元素也是一样的)
  • 定位元素具备一个正的 z-index 值(它的子元素也是一样的)

应用变量去跟踪 z-index 属性

滥用 z-index 属性会使款式文件变得难以保护,没有一个清晰的层级程序的话,组件之间容易陷入一场“z-index”之争。如果没有标准要求,开发者可能会增加一个模态框组件,因为放心模态框组件被其余组件笼罩,所以给它加了一个相当高的层级,例如“999999”。

当雷同的状况在同一个页面多产生几次之后,就不晓得该给新的组件设置多大的层级了。

如果你有应用 CSS 预处理器,例如 SASS 或 LESS,或者你须要做兼容的浏览器都反对 CSS 变量,那么就能够优雅的解决这个问题。

将所有 z-index 属性值存入变量中,集中放到一个中央。这样你只须要轻松一瞥就能看出谁会显示在谁的后面:
--z-loading-indicator: 100;
--z-nav-menu: 200;
--z-dropdown-menu: 300;
--z-modal-backdrop: 400;
--z-modal-body: 410;

应用 10 或 100 的升序排列,这样你就能够轻松的插入一些你想要显示在其余元素后面的 z-index。

如果你发现你设置了 z-index 属性的元素没有依照你心愿的形式显示,则应该看看 DOM 构造,找到以后层叠上下文的根元素,而后设置更高的 z-index 属性来晋升或升高整个层叠上下文的层级。在开发中要留神,可能存在一个层叠上下文中嵌套着多个层叠上下文的状况。

当页面变得复杂,通常难以看出以后的组件属于哪一个层叠上下文。所以当你在创立层得上下文的时候就要小心了。尽量不要创立层叠上下文,除非有非凡的起因。尤其是在构建大型应用程序的时候。

尽可能的将定位元素,例如模态框等,放在 DOM 的最高层级下,例如在 </body> 标签的后面,这样你就不须要放心内部可能存在的层叠上下文了。

有些开发者将页面中的很多的元素都设置定位。好的形式不应该是这样,你应用的定位元素越多,页面就会变得越简单,并且变得难以调试。如果你正在大量应用定位元素,那么当初是时候好好调整下了。你构建页面布局的形式是十分重要的,当你可能应用其余办法实现布局时,最好应用这些办法,而不是定位。

如果你可能利用文档流(Document flow)的特点去为你实现布局,而不是间接应用定位,浏览器会帮你思考一些可能存在的边缘状况。记住,应用定位使元素脱离规范文档流。通常状况下,只有当你须要将一个元素笼罩在另一个元素的后面时,才思考应用定位。

参考:
《CSS in Depth》Keith J. Grant

正文完
 0