你可能对position和z-index有一些误解

简介: 该文章深入解析了CSS中`position`属性的各个取值(static、relative、absolute、fixed、sticky)的使用场景及`z-index`在层叠上下文中如何确定元素的堆叠顺序,并通过具体例子展示了这些属性的实际应用效果。

封面

🧃序言

最近在整理一些很偏的面经题,想起来很久以前被问到的一道题,就是 positionz-index 。短短 10min 问了这两个属性,大概直接把周一问懵了。那个时候的心情可能是这样的……

丢脸

于是,一鼓作气,不懂的知识咱还是得补呀,万一下回又直接被问懵了,那就是死不知悔改了不是。

最后,通过各种资料的查询和理解,整理了下文。内容主要涵盖 position 各个取值的解析以及关于 z-index 的使用。

下面开始进入本文的讲解~🧐

🍷一、文章结构抢先知

在文章开始讲解之前,我们先用一张思维导图来了解本文所要讲解的知识。详情见下图👇

思维导图

了解完思维导图之后,现在开始进入本文的讲解。

🍸二、position

1. position的取值

通常情况下, position 有以下几个取值。具体如下:

取值 含义 说明
static 静态定位 对象遵循标准文档流,toprightbottomleft 等属性失效。
relative 相对定位 对象遵循标准文档流中,依赖toprightbottomleft 等属性相对于该对象在标准文档流中的位置进行偏移,同时可通过 z-index 定义层叠关系。
absolute 绝对定位 对象脱离标准文档流,使用 toprightbottomleft 等属性进行绝对定位(相对于 static 定位以外的第一个父元素进行绝对定位) ,以及可通过 z-index 定义层叠关系。
fixed 固定定位 对象脱离标准文档流,使用 toprightbottomleft 等属性进行绝对定位(相对于浏览器窗口进行绝对定位)同时可通过 z-index 定义层叠关系。
sticky 粘性定位 可以说是相对定位 relative 和固定定位 fixed 的结合。元素固定的相对偏移是相对于离它最近的具有滚动框的祖先元素,如果祖先元素都不可以滚动,那么是相对于 viewport 来计算元素的偏移量。

2. 标准文档流

我们先来看下标准文档流的定义:

所谓标准文档流,指的是在不使用其他与排列和定位相关的特殊 CSS 规则时,元素的默认排列规则。

理解完定义之后,我们来看看各个取值的使用效果。

3. 各取值解析

(1)static

static ,是默认的 position 值。它没有特殊的定位,且遵循标准文档流。我们用例子来展示一下。

先附上代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>static</title>
</head>
<style>
    div{
    
        width: 100px;
        height: 100px;
        margin: 40px auto;
    }

    .one{
    
        background-color: #99d98c;
    }

    .two{
    
        background-color: #76c893;
    }
</style>
<body>
    <div class="one"></div>
    <div class="two"></div>
</body>
</html>

现在来看演示效果:

static

大家可以看到,正如我们所想的,规规矩矩的出现在我们面前,跟我们平常实现其他内容所遵循的规则也基本一样。所以这里不做过多解释。

(2)relative

relative ,对象遵循标准文档流,依赖 toprightbottomleft 等属性相对于该对象在标准文档流中的位置进行偏移,同时可通过 z-index 定义层叠关系。我们现在用一个例子来展示一下。

先附上代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>relative</title>
</head>
<style>
    body{
    
        margin: 0 300px;
    }

    div{
    
        width: 200px;
        height: 200px;
        padding: 20px;
    }

    .one{
    
        background-color: #99d98c;
        position: relative;
        left: 40px;
        top: 60px;
    }

    .two{
    
        background-color: #76c893;
    }
</style>
<body>
    <div class="one">1</div>
    <div class="two">2</div>
</body>
</html>

现在来看演示效果:

relative

大家可以看到, relative 是相对于该对象所处的标准文档流中的位置,依据 lefttop 进行定位(当然也可以使用 rightbottom ,本例仅用以上两个属性做说明)。同时, lefttop 并不会改变该对象原本在文档流中的占位空间。


值得注意的是,如果设置为 marginpadding 属性时,该对象在标准文档流中的占位空间也将会随之改变。

附上代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>relative</title>
</head>
<style>
    body{
    
        margin: 0 300px;
    }

    div{
    
        width: 200px;
        height: 200px;
        padding: 20px;
    }

    .one{
    
        background-color: #99d98c;
        position: relative;
        left: 40px;
        top: 60px;
        margin: 0 0 60px 40px;
    }

    .two{
    
        background-color: #76c893;
    }
</style>
<body>
    <div class="one">1</div>
    <div class="two">2</div>
</body>
</html>

现在我们来看下浏览器的演示效果:

relative

大家可以看到,当设置了 margin 属性时,该对象仍然跟随着其所处的标准文档流中的占位空间的变化而变化。

(3)absolute

absolute ,对象脱离标准文档流,使用 toprightbottomleft 等属性进行绝对定位(相对于 static 定位以外的第一个父元素进行绝对定位) ,以及可通过 z-index 定义层叠关系。

先来看个例子,具体代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<style>
    body{
    
        margin: 50px 300px;
    }

    .father-ele{
    
        width: 300px;
        height: 300px;
        background-color: #168aad;
        position: relative;
    }

    .son-ele{
    
        width: 200px;
        height: 200px;
        position: static;
        background-color: #76c893;
        margin-left: 20px;
    }

    .three{
    
        width: 100px;
        height: 100px;
        background-color: #99d98c;
    }

    .four{
    
        width: 100px;
        height: 100px;
        position: absolute;
        left: 0;
        top: 0;
        background-color: #b5e48c;
    }
</style>
<body>
    <div class="father-ele">
        1
        <div class="son-ele">
            2
            <div class="three">3</div>
            <div class="four">4</div>
        </div>
    </div>
</body>
</html>

具体效果如下:

absolute

从上面的定义中我们了解到,绝对定位 absolute 是脱离标准文档流的,且是相对于 static 定位以外第一个父元素,并且使用 left和topright和bottom 进行绝对定位。所以大家可以看到上图,设置了 absolutefour 相对于 father-ele 进行定位。

值得一提的是,在使用 absolute 绝对定位时,必须指定 lefttoprightbottom 中的至少一个,否则 left/right/top/bottom 属性将会使用默认值 auto 。这将导致对象遵从标准文档流,在前一个对象之后立即被呈递,简单讲就是都变成 relative ,并且会占用文档空间。

还有就是,如果同时设置了 left/right 属性,那么 left 生效。同理如果 top/bottom 同时存在时, top 生效。

(4)fixed

fixed ,对象脱离标准文档流,使用 toprightbottomleft 等属性进行绝对定位(相对于浏览器窗口进行绝对定位)同时可通过 z-index 定义层叠关系。

先来看个例子,具体代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>fixed</title>
</head>
<style>
    body{
    
        margin: 50px 300px;
    }

    .father-ele{
    
        width: 300px;
        height: 300px;
        background-color: #168aad;
        position: relative;
    }

    .son-ele{
    
        width: 200px;
        height: 200px;
        position: static;
        background-color: #76c893;
        margin-left: 200px;
    }

    .three{
    
        width: 100px;
        height: 100px;
        position: fixed;
        left: 20px;
        top: 20px;
        background-color: #99d98c;
    }

    .four{
    
        width: 100px;
        height: 100px;
        background-color: #b5e48c;
    }
</style>
<body>
    <div class="father-ele">
        1
        <div class="son-ele">
            2
            <div class="three">3</div>
            <div class="four">4</div>
        </div>
    </div>
</body>
</html>

具体效果如下:

fixed

大家可以看到,fixed 是相对于浏览器窗口进行固定定位的。

(5)sticky

对于 sticky 来说,就是粘性定位,主要用来做导航栏滑动到一定程度时让导航栏固定到顶部。不过这个属性有它一定的利弊存在,要了解清楚其中存在的各种问题后,谨慎使用!!

这里不再过多介绍,感兴趣的小伙伴可以戳此链接查看该属性的内容~

讲到这里,关于 position 的各种取值解析基本就结束啦!但是细心的小伙伴可能已经发现,上面一直提到的 z-index ,好像都没有讲到。不着急,接下来我们就来讲讲 z-index 这个属性的纸短情长。

🍹三、z-index

1. 一个片面的理解

在我的认知里,一直认为 z-index 是用来描述页面的层叠顺序。一旦 z-index 的值越大,页面就可以叠放的越高。但事实证明,是我孤陋寡闻了。所谓 z-index ,只有在以下场景适用。分别为:

  • 首先, z-index 这个属性并不是在所有的元素上都有效果。它仅仅只在定位元素(定义了 position 属性,且属性的值为非 static 值的元素)上有效果。
  • 要判断元素在 z轴 上的堆叠顺序,并不仅仅是直接比较两个元素的 z-index 值的大小,同时,这个堆叠顺序还由元素的层叠上下文层叠等级共同决定。

先来看一张图👇

z-index

相信大家对三维坐标空间一定很熟悉。通常地,我们用 x轴 来表示水平位置,用 y轴 来表示垂直位置,然后用 z轴 来表示在纸面内外方向上的位置。但是呢,由于屏幕是一个二维平面,所以我们并不是真正地看到 z轴 。我们经常说的看到 z轴 ,实际上是通过透视,将元素展现在其二维空间的前面或者后面才看到的。

z-index 有了一个基础的认识之后,我们来看看它的取值。

2. z-index的取值

要确定沿着这 z轴 元素是如何分布的,css 允许我们对 z-index 属性设置3种类型的值。分别是:

  • auto(自动,默认值);
  • 整数(正整数/负整数/0);
  • inherit(继承)。

目前,让我们先关注在整数值上。 整数值可以是正值,负值,或0。通常来说,数值越大,元素也就越靠近观察者;而数值越小,元素看起来也就越远。

所以,如果有两个元素放在了一起,占据了二维平面上一块共同的区域,那么有着较大 z-index 值的元素就会掩盖或者阻隔有着较低 z-index 值的元素在共同区域的那一部分。

尽管如此,现在还是有一些问题悬而未决、等待我们的解答。具体问题如下:

  • 当一个设置了 z-index 值的定位元素与常规文档流中的元素相互重叠的时候,谁会被置于上方?
  • 当定位元素与浮动元素相互重叠的时候,谁会被置于上方?
  • 当定位元素被嵌套在其他定位元素中时,又会发生什么呢?

带着这些问题,我们继续深入了解神奇的 z-index 是怎么工作的,又会抛出哪些新的内容呢?

接下来我们来了解一下上述中提到的层叠上下文层叠等级层叠顺序

3. 层叠上下文

(1)举个例子

我们来做一个具象的比喻:

假设现在有一张桌子,桌子上面呢,放了好多水果,那么这张桌子就代表着一个层叠上下文。

再来,我们还有一张桌子,桌子上面,放了很多本书,那么新的这张桌子,又代表着一个新的层叠上下文。

而水果和书这两样东西,就可以理解为他们所处的层叠上下文中的层叠上下文元素

这样理解,会不会就明白了许多呢。现在,我们来抛出它的定义。

(2)定义

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

如果一个元素含有层叠上下文,(也就是说它是层叠上下文元素),我们可以理解为这个元素在Z轴上就“高人一等”,最终表现就是它离屏幕观察者更近。

4. 层叠等级

(1)定义

讲完层叠上下文,我们再来看看层叠等级又是什么呢?

所谓层叠等级(stacking level),也叫 层叠级别层叠水平它有两层含义:

  • 在同一个层叠上下文中,它描述的是该层叠上下文中的层叠上下文元素在 Z轴 上的上下顺序。
  • 在其他普通元素中,它描述的是这些普通元素在 Z轴 上的上下顺序。

(2)举个例子

同样,我们用刚刚那个例子来继续举例。假设桌子上面的苹果和书,是一层一层叠上去的。

现在,我们先看第一张桌子,第一张桌子的所有苹果,是在同一个层叠上下文中,那么苹果的堆叠顺序,就是在这个层叠上下文中 z轴 上的上下顺序,这是第一层含义

然后呢,第一张桌子和第二张桌子,他们是两个不同的层叠上下文,我们把它们视为两个普通的元素。那这两个普通的元素,它们也有在 z轴 上的上下顺序。所以呢,这个上下的堆叠顺序,就是我们所说的第二层含义

5. 形成的条件

看完层叠上下文和层叠等级,我们现在需要来了解一下,要怎么样,才能让元素形成层叠上下文。具体有以下几种情况:

  • 根元素html
  • 绝对定位 ansolute 或相对定位 relativez-index 值不为 auto
  • 一个 flex 项目,且 z-index 值不为 auto ,也就是父元素 display: flex|inline-flex
  • 元素的 opacity 属性值小于 1
  • 元素的 transform 属性值不为 none
  • 元素的 mix-blend-mode 属性值不为 normal
  • 元素的 isolation 属性被设置为 isolate
  • mobile WebKitChrome 22+ 内核的浏览器中,position: fixed 时总是会创建一个新的层叠上下文, 即使 z-index 的值是 auto
  • 元素的 -webkit-overflow-scrolling 属性被设置 touch

6. 层叠顺序

在第 4 点中,尽管上面所说的只包含一个两级的层叠,但事实上在一个层叠上下文中,一共可以有7种层叠等级具体如下图:

7种层叠层级

下面我们来一一对上面的7种层叠顺序进行阐述。说明如下:

  • 背景和边框 —— 形成层叠上下文的元素的背景和边框,也是层叠上下文中的最低等级
  • 负z-index值 —— 层叠上下文内有着 负z-index值 的子元素。
  • 块级盒 —— 文档流中非行内非定位子元素。
  • 浮动盒 —— 非定位浮动元素。
  • 行内盒 —— 文档流中行内级别非定位子元素。
  • z-index: 0 —— 定位元素,这些元素将形成了新的层叠上下文。
  • 正z-index值 —— 定位元素。 层叠上下文中的最高等级。

注: 字体颜色对应框内颜色

以上这七个层叠等级构成了层叠次序的规则。比如, 在层叠等级七上的元素会比在等级一至六上的元素显示得更上方(更靠近观察者)。 在层叠等级四上的元素会显示在等级一至三上的元素之上。

看到上面这张图的时候,我想到了一些坑。

如果你只看层叠等级 2 , 6 , 7 (也就是那些提到了 z-index 的等级),那么有很大的可能你会发现这跟你理解的 z-index 可能是一样的。 正 z-index 值比 z-index:0 值更高一层, z-index:0 值又比负 z-index 值高一层。 往往我们很多时候都只理解到了这里,也就觉得 z-index 只有这些内容,往后的事也不了了之了。

但事实上,事实与现实总是不相符的。一般来说,大多数的元素都比 z-index:0z-index > 0 时的层叠等级要低。

另外同样有趣的是非定位元素分散在四个不同的层叠等级上,为什么是这样子的呢?其实,你可以想象以下,如果所有的非定位元素都在同一层叠等级上,那么我们也就不会看到文字(行内盒)在 div 盒子上了(块级盒)。

7. 例子展示

讲到这里,相信大家对 z-index 已经有了一定的认知。接下来呢,我们就就来用一个例子巩固一下上面所学到的知识🌈

先附上代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="09.scss">
</head>
<style>
    div {
    
  width: 200px;
  height: 200px;
  padding: 20px;
}

.ele1, .ele2, .ele3, .ele4 {
    
  position: absolute;
}

.ele1 {
    
  background: #38a3a5;
  top: 100px;
  left: 200px;
  z-index: 10;
}

.ele2 {
    
  background: #99d98c;
  top: 50px;
  left: 75px;
  z-index: 100;
}

.ele3 {
    
  background: #57cc99;
  top: 125px;
  left: 25px;
  z-index: 150;
}

.ele4 {
    
  background: #80ed99;
  top: 200px;
  left: 350px;
  z-index: 50;
}
</style>
<body>
    <div class="ele1">
        1
        <div class="ele2">2</div>
        <div class="ele3">3</div>
      </div>
      <div class="ele4">4</div>
</body>
</html>

大家可以先想象一下具体的层叠顺序,然后再来看结果。接下来看一下演示效果:

演示图片

不知道最终的结果是都跟小伙伴们心里的预期是一样的呢。很多小伙伴可能想着, ele4z-index 值不是 50 吗,而 ele2ele3z-index 值是 100200 ,为什么反而是 ele4 在最上面呢。我们来解决这个疑惑。

其实, ele1ele4 是两个不同的层叠上下文,而 ele2ele3ele1 的层叠上下文元素。所以, ele1 的值是 10 ,而 ele2ele3 可以理解为是 10.10010.150 ,这样,自然地,ele4 肯定就是在最上方了。

还有另外一种特殊情况就是, ele4z-index 的值跟 ele1 的值是一样的,都是 10 。在这种情况下,如果遇到在同一个层叠上下文的元素,他们的 z-index 值如果一样的话,那得遵循个先来后到原则。 ele1 先出现了,所以它在里面;而 ele4 后出现,它在外面。

那如果遇到 ele4z-index 的值是 9 呢?我们改一下 ele4css 样式,然后来观察一下效果。具体代码如下:

.ele4 {
   
  background: #80ed99;
  top: 200px;
  z-index: 9;
}

具体效果如下:

演示效果

通过上图,大家可以发现,ele4z-index 值比 ele1 小,所以自然地, ele4 在里面, ele1 在外面。

🥂四、结束语

当你初次遇到 z-index 时,它就像一个非常简单、易于理解的属性。 它的值代表着在朝向屏幕内外面轴上的位置。而深入探究 z-index 以后我们还发现了很多 z-index 背后的事情, 包括层叠上下文、层叠等级和层叠次序规则等等。

所以说呀,万宗不变其一,万事万物还是得追溯到源头上才能更好的解决问题本身。

相信通过上文的了解,大家对 positionz-index 又有了一个全新的认识~

🐣彩蛋 One More Thing

(:参考资料

长安曹公子👉彻底搞懂CSS层叠上下文、层叠等级、层叠顺序、z-index

Steven Bradley👉关于z-index 那些你不知道的事

猫儿不熊👉CSS-position:static/relative/absolute/fixed定位

书籍👉张鑫旭老师的CSS世界

(:番外篇

  • 关注公众号星期一研究室,第一时间关注优质文章,更多精选专栏待你解锁~

  • 如果这篇文章对你有用,记得留个脚印jio再走哦~

  • 以上就是本文的全部内容!我们下期见!👋👋👋

相关文章
|
12月前
can't do nonzero end-relative seeks
can't do nonzero end-relative seeks
33 0
|
前端开发 容器
你真的了解position吗?
你真的了解position吗?
|
Web App开发 前端开发 测试技术
你可能对position和z-index有一些误解
最近在整理一些蛮偏的面经题,想起来很久以前被问到的一道题,就是 position 和 z-index 。短短 10min 问了这两个属性,大概直接把周一问懵了。那个时候的心情可能是这样的……
你可能对position和z-index有一些误解
LeetCode之Search Insert Position
LeetCode之Search Insert Position
97 0
|
Web App开发 前端开发