跳到主要内容

css 层叠上下文导致的 hover 抓不到子元素的 bug

阅读需 3 分钟

parent 的 opacity < 1,使 parent 形成了上下文,child-container 的 z-index 相对于的是parent上下文,不参与全局的上下文。会被后面的文档流覆盖掉。所以会出现 hover 抓不到子元素的现象。

代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="container">
<ol>
<li>hover toggle child, 展示 child-container</li>
<li>hover child-container, 会出现“抓不到”的现象</li>
</ol>
<div class="grand-parent">
<div class="parent">
toggle child
<div class="child-container">
<div class="child">1</div>
<div class="child">2</div>
<div class="child">3</div>
</div>
</div>

</div>
<div class="sibling">
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
</div>
</div>
<style>
.grand-parent {
position: relative;
}
/* parent 的 opacity < 1, 使 parent 形成了上下文 */
.parent {
position: relative;
opacity: 0.8;
cursor: not-allowed;
}

.child-container {
display: none;
position: absolute;
top: 100%;
left: 0;
width: 100%;
background-color: aqua;
z-index: 100; /* z-index 相对于的是parent上下文 */

}

.parent:hover .child-container {
display: block;
}

/* 普通定位元素,位于 grand-parent 之后,所以会覆盖掉 grand-parent 内的元素 */
.sibling {
position: relative;
}
</style>
</body>
</html>

解决方法

将 opacity 分别给 parent 的子元素,即 child-container 及child-container 的兄弟元素。

原因是:在 parent 设置 opacity < 1, 使 parent 形成了上下文,此时 .child-container 的 z-index 相对于的是parent上下文,不参与全局的上下文。会被后面的文档流覆盖掉。

层叠上下文绘制顺序

  • 层叠上下文的背景和边框
  • 负z-index的子层叠上下文
  • 块级盒子(block-level boxes) ← 普通元素在这里!
  • 浮动元素(floated elements)
  • 内联元素(inline elements)
  • z-index: 0的子层叠上下文 / 定位元素(z-index: auto)
  • 正z-index的子层叠上下文

其他变形

使元素变为层叠上下文的 css 属性

  1. display 为 flex(flex | inline-flex),同时 z-index 不是 auto;
  2. opacity < 1;
  3. transform 不为 none;
  4. filter 不为 none;
  5. mix-blend-mode 不是 normal;
  6. isolation 是 isolate;
  7. will-change 属性为上面 2~6 的任意一个(will-change: transform、will-change:opacity)
  8. -webkit-overflow-scrolling 为 touch

使 grand-parent 形成层叠上下文

在 grand-parent 形成了层叠上下文;sibling 的 z-index 为 auto,不形成层叠上下文;

  1. grand-parent 的 z-index 为 0;

    • hover child-container 出现“抓不到”的现象
    • grand-parent 的 z-index 为 0;sibling 的 z-index auto 在层级比较中,相当于 0;所以 grand-parent 会被后来的 sibling 覆盖;
  2. grand-parent 的 z-index 为 1;

    • hover child-container 不会出现“抓不到”的现象
    • grand-parent 的 z-index 为 1,比 sibling 的 z-index auto 层级高,所以 grand-parent 会覆盖 sibling;
  3. grand-parent 的 transform 为 translateX(0);

    • hover child-container 出现“抓不到”的现象;
    • grand-parent 被文档流中的 sibling 覆盖;

使grand-parent 不形成层叠上下文

在 grand-parent 不形成层叠上下文;设置 sibling 的 z-index 为 0

  • hover child-container 不会出现“抓不到”的现象;
  • child-container 此时的 z-index 相对于根元素;与 sibling 一起 pk,child-container 会覆盖 sibling;

在 grand-parent 不形成层叠上下文;设置 sibling 的 z-index 为 101

  • hover child-container 出现“抓不到”的现象;
  • child-container 此时的 z-index 相对于根元素;与 sibling 一起 pk,sibling 会覆盖 child-container;

总结

  1. 当父级元素中,若哪一层级形成了层叠上下文,元素的 z-index 就会相对于该层叠上下文;否则是相对于根层叠上下文。

  2. z-index 会在同一级的上下文中,与其他有 z-index 的元素去 pk,z-index 值较大的元素会覆盖z-index 值较小的元素。

  3. 若 z-index 相对于非根层叠上下文;且该层叠上下文被后续文档流,或被后续层级更高的层叠上下文覆盖,则该元素会被覆盖。

Loading Comments...