Float 和 Absolute 元素的 Z-index 关系以及 Z-index 的奇怪表现

1,397 阅读3分钟

最近临摹华为官网的时候再一处导航栏遇到了意外的情况,导航栏分为 A、B、C 三部分,分别位于同一行的左、中、右,如下图:

为了将这3个元素并列在一行上,设置了 A、C 均为 Float,B 为 Absolute, 且 A、B、C 均可以被点击。但是写完 HTML 和 CSS 后却发现除了 B 之外,都无法点击。

排查后发现是 A、C 被 B 遮挡的原因,于是尝试通过 z-index 的来解决。

为此搭建了一个简单模型来尝试解决,代码如下:

<!DOCTYPE html><html><head>    <meta charset="UTF-8">    <title>test</title>    <style>        .container {            position: relative;            width: 100%;            background-color: #000;            margin-top: 100px;            padding: 20px;            box-sizing: border-box;        }        .container::before,        .container::after {            display: block;            content: "";            clear: both;        }        .fl {            float: left;            width: 40px;            height: 80px;            background-color: #0051ff;            z-index: 0;        }        .ab {            position: absolute;            bottom: 0;            left: 0;            width: 98%;            height: 60px;            background-color: #ffc400;            z-index: 0;        }        .fr {            float: right;            width: 40px;            height: 80px;            background-color: #ff0000;            z-index: 0;        }    </style></head><body>    <div class="container">        <div class="fl">float left</div>        <div class="fr">float right</div>        <div class="ab"> absolute</div>   </div></body></html>

(这是在线代码:codepen.io/sangyaaak/p…

结果如下图:

果然 abosolute 元素挡住了 float 元素,推测是因为 float 元素属于半脱离文档流,而 abosolute 元素完全脱离文档流,因此 absolute 元素总是在当前的文档流之上,当然也就覆盖住了 float。

有什么办法可以解决?

方法是为 absolute 元素设置一个 z-index: -1,将 absolute 元素的堆叠高度降到文档流底下,这样就能不会挡住 float 元素了。结果如下:

黄色 absolute 块怎么不见呢?原因是它掉到了 黑色的 container 底下了,将 container 的背景色调透明就能看到。

这不是我想要的,我希望 abosolute 不挡住 float,并且在 container 之上,该怎么办?

答案是为 container 设定一个 z-index: 0;

看,黄色的 absolute 块回到了 float 块的底下,同时又不会掉到出 container 底下。

不要尝试对 float 元素设置 z-index, 因为 z-index 对 float 无效;准确的说应该是元素默认为 position: static;只设置 float 时并没有改变他的 position,而 z-index 仅对 static 元素无效。

Note: z-index only works on positioned elements (position: absolute, position: relative, position: fixed, or position: sticky).

所以除非你给他设置其他的 position, 否则就无法用 z-index控制其堆叠的顺序。

后面还对 z-index 做了很多的实验,发现不少奇怪现象(可能是 bug),在次不再赘述过程,仅把所有的结论列出来。

1、z-index 只对拥有 position 属性的元素生效, position = static 除外。

2、z-index 表示的是元素与其在同一 “容器” 内的其他元素的堆叠顺序(order),有效值为整数。

3、未声明 z-index 和 static 元素的 z-index 默认为 0,堆叠顺序按渲染时序由前到后排,先渲染则在下,依次向上堆叠。

4、正常情况下,子元素(child)总是在其父元素(parent)之内,不管子元素(child)的 z-index 多大/多小,子元素都不会在比其父元素低的元素之下或之上。

// 举个栗子: 
//以下有两组标签,父元素<A>、<B>分别有子元素,且 z-index 各不相同。
<body>    <A>               // A z-index = -1  
        <AA_1>        // AA_1 z-index = 10        <AA_2>        // AA_2 z-index = 20
    </A>

    <B>                // B z-index = 1        <BB_1>         // BB_1 z-index = -100
    </B>

</body>

堆叠顺序结果如下:

<BB_1>—— ——<AA_2> ——<AA_1> ——

虽然<BB_1>的 z-index 为 -100, 但只是对于内的元素而言,并不与之外的元素比较顺序。

但是!!!以上的情况仅发生在所有元素都设定了有效的 z-index 值。否则会出现以下情况:

(以下为类似bug的发现)

1、子元素设有 z-index 而父元素没有设定时,子元素会不断向外寻找,直到发现某个外层元素设有 z-index 并将其作为容器,若未发现则以 为容器。

2、父元素设定  z-index 的子元素则表现正常。

3、除了设定 z-index 外,设定 opacity ,但值必须 <1,这样也可以解决以上奇怪现象。

// 举个栗子: 
//以下有两组标签,父元素<A>、<B>分别有子元素,且 z-index 各不相同。
<body>  
    <A>               // 
        <AA_1>        // AA_1 z-index = 10
        <AA_2>        // AA_2 z-index = 20
    </A>

    <B>                //       
        <BB_1>         // BB_1 z-index = -100
    </B>

</body>

堆叠顺序结果如下:

<AA_2> ——<AA_1>—— —— ——<BB_1>

// 再举个栗子: 
//以下有两组标签,父元素<A>、<B>分别有子元素,且 z-index 各不相同。
<body>  
    <A>               // 
        <AA_1>        // AA_1 z-index = 10
        <AA_2>        // AA_2 z-index = 20
    </A>

    <B>                // B z-index = 15      
        <BB_1>         // BB_1 z-index = -100
    </B>

</body>

堆叠顺序结果如下:

<AA_2> ——<BB_1>—— —— <AA_1>——

以上

如果发现元素之间的遮挡关系很奇怪的话,还是好好检查下父元素和子元素之间的 z-index 吧。