z-index 层叠上下文综合实践(流光按钮/带悬停效果的导航/弹性滑动导航)

24 阅读6分钟

1. 层叠上下文

  1. 什么是层叠上下文
    • 层叠上下文理解为 HTML 元素的一个属性,一但 HTML 元素拥有了这个属性,我们可以理解为这个 HTML 元素在Z 轴上就“高人一等”,也就是在 Z 轴上会显示在更上一层,。
  2. 如何创建层叠上下文
    • html 根层叠上下文 (默认的,不需要创建)
    • 定位元素的传统层叠上下文(z-index 值为数值时才会创建层叠上下文,z-index:auto 不会创建层叠上下文)。
      • 对于包含有position:relative/position:absolute的定位元素,当其z-index值不是auto的时候,会创建层叠上下文.
      • position值为 fixed(固定定位)或 sticky(粘滞定位)的元素
    • css3 与新时代的层叠上下文
      • z-index值不为autoflex项(父元素display:flex|inline-flex
      • 元素的opacity值不是1
      • 元素的transform值不是none
      • 元素mix-blend-mode值不是normal
      • 元素的filter值不是none
      • 元素的isolation值是isolate
      • will-change指定的属性值为上面任意一个
      • 元素的-webkit-overflow-scrolling设为touch
  3. 同一层叠上下文中,元素的层叠顺序 {56DECFD8-164E-498E-8FA6-C32087DF09DC}.png
  4. z-index 可以调节同一层叠上下文中 定位元素的层叠顺序
  5. 如何确定两个元素的层叠顺序
  • 在比较两个元素的层叠顺序时,会先比较他们所在的层叠上下文的层叠顺序
  • 如果他们所在的是同一层叠上下文,则按同一层叠上下文中的元素排列顺序来计算。
  • 如果不在同一层叠上下文中,则他们所在层叠上下文的顺序决定了他们的顺序及其后代元素的层叠上下文顺序。
  • 即 A 层叠上下文要高于 B 叠上下文,则 A 中的所有子孙元素都会高于 B 中的所有子孙元素。B 中子孙元素的z-index 再大也没有办法在层级上高于 A 元素中子元素。,因为 z-index 只能控制同一层叠上下文中的元素堆叠关系。

2. 流光按钮

GIF-2024-12-31-17-52-53.f5ea0f9f.gif
步骤:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>流光按钮</title>
    <style>
        body {
            background-color: #000;
        }

        a.button {
            display: block;
            width: 160px;
            height: 50px;
            background-color: #333;
            text-align: center;
            line-height: 50px;
            text-decoration: none;
            color: #666;
            font-weight: bold;
            border-radius: 5px;
            overflow: hidden;
            position: relative;
            /* 相对定位 */
        }

        a.button:hover {
            color: yellow;
        }

        a.button::before {
            content: "";
            display: block;
            width: 300px;
            height: 300px;
            background-image: radial-gradient(yellow, transparent 40%);
            position: absolute;
            /* left: 0px;
            top: 0px; */
            left: var(--left);
            /* 使用自定义属性 --left 来控制彩色毛毛球的位置(水平方向)*/
            top: var(--top);
            /* 使用自定义属性 --top 来控制彩色毛毛球的位置(垂直方向)*/
            margin-left: -150px;
            margin-top: -150px;
            display: none;
        }

        a.button:hover::before {
            display: block;
            /* 鼠标移入时显示彩色毛毛球 */
        }

        a.button::after {
            content: "";
            display: block;
            position: absolute;
            /* 绝对定位 */
            left: 2px;
            right: 2px;
            top: 2px;
            bottom: 2px;
            background-color: #333;
            border-radius: 5px;
        }

        .m-center {
            margin: 50px auto;
        }

        a.button span {
            /* 相对定位用来调整元素层叠顺序 */
            position: relative;
            z-index: 1;
            /* z-index: 1; 提高层级,确保文字在最上层显示 */
        }
    </style>
</head>

<body>
    <a href="#" class="button m-center">
        <span>加入购物车</span>
    </a>
    <script>
        // 获取所有按扭
        const button = document.querySelector("a.button");
        // 遍历按扭,给每个按扭添加鼠标移入事件监听器

        button.onmousemove = function (e) {
            const pageX = e.pageX; // 获取鼠标与浏览器左边距离
            const pageY = e.pageY; // 获取鼠标与浏览器顶部距离
            const left = this.offsetLeft; // 获取按扭距离浏览器左边距离
            const top = this.offsetTop; // 获取按扭距离浏览器左边距离

            // 计算得到 鼠标相对于按扭左边和上边距离
            const x = pageX - left;
            const y = pageY - top;

            // 给伪元素设置自定义属性 --left 和 --top,用来控制彩色毛毛球的位置
            this.style.setProperty("--left", x + "px");
            this.style.setProperty("--top", y + "px");

            // 注意这一步,要将  button::before css 选择器的 中的 top 和 left 修改为
            // top:var(--top);  left:var(--left);
        };
    </script>
</body>

</html>

2. 带悬停效果的导航

GIF2025-6-617-30-14.244d4c24.gif

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>带悬停效果的导航</title>
    <style>
  body,
  ul {
    margin: 0;
  }
  ul {
    padding: 0;
    list-style: none;
  }
  a {
    text-decoration: none;
    color: #000;
  }
  .nav {
    height: 50px;
    background-color: skyblue;
    text-align: center;
    margin-top: 50px;
  }
  .nav ul {
    /* background-color: khaki; */
    width: 1200px;
    margin: 0 auto; /* 水平居中 */
    height: inherit; /* 继承父元素值 */
  }
  .nav ul li {
    float: left;
    font-size: 16px;
    height: inherit; /* 继承父元素值 */
  }
  .nav ul li a {
    display: block;
    padding: 0px 20px;
    line-height: 50px;
    position: relative; /* 添加相对定位 */
    z-index: 0; /* 创建自己的层叠上下文  */
  }
  .nav ul li a::after {
    content: "";
    position: absolute; /* 相对a标签绝对定位*/
    left: 0;
    right: 0;
    top: -5px;
    bottom: -5px;
    background-color: rgb(2, 139, 194);
    display: none;
    z-index: -1; /* 让滑块显示在文字下面 */
  }
  .nav ul li a:hover {
    color: #fff;
  }
  .nav ul li a:hover::after {
    display: block;
  }
</style>
</head>
<body>
  <div class="nav">
    <ul>
      <li><a href="#">首页</a></li>
      <li><a href="#">免费直播课</a></li>
      <li><a href="#">web前端工程师</a></li>
      <li><a href="#">Java架构师</a></li>
      <li><a href="#">实战案例视频</a></li>
      <li><a href="#">关于我们</a></li>
    </ul>
  </div>   
</body>
</html>

3. 弹性滑动导航

GIF2025-6-618-05-54.26362462.gif

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>弹性滑动导航</title>
<style>
  body,
  ul {
    margin: 0;
  }
  ul {
    padding: 0;
    list-style: none;
  }
  a {
    text-decoration: none;
    color: #000;
  }
  .nav {
    height: 50px;
    background-color: skyblue;
    text-align: center;
    margin-top: 50px;
  }
  .nav ul {
    width: 1200px;
    margin: 0 auto;
    height: inherit;
    position: relative; /* 相对定位 */
    z-index: 0; /* 创建自己的层叠上下文 */
  }
  .nav ul li {
    float: left;
    font-size: 16px;
    height: inherit; /* 继承 */
  }
  .nav ul li a {
    display: block;
    padding: 0px 20px;
    line-height: 50px;
  }

  .nav ul li:first-child a {
    color: #fff;
  }
  .nav ul li:last-child {
    width: 72px;
    height: 60px;
    background-color: rgb(3, 135, 187);
    /*这个li用来做底部蓝色滑块,需要相对ul进行绝对定位*/
    position: absolute;
    left: 0px;
    top: -5px;
    z-index: -1;
    /* 添加过渡动画 */
    transition: left 0.3s, width 0.3s;
  }
  /* 这里的功能需要用js来实现,
    以下只是用css简单演示实现原理 */
  .nav ul li:hover ~ .move {
    width: 110px;
    left: 348px;
  }
</style>
</head>
<body>
<div class="nav">
  <ul>
    <li><a href="#">首页</a></li>
    <li><a href="#">免费直播课</a></li>
    <li><a href="#">web前端工程师</a></li>
    <li><a href="#">Java架构师</a></li>
    <li><a href="#">实战案例视频</a></li>
    <li><a href="#">关于我们</a></li>
    <!--这个li是用来做深蓝色的小滑块-->
    <li class="move"></li>
  </ul>
</div> 
<script>
  const move = document.querySelector(".move");
  const oUl = document.querySelector(".nav ul ");
  // 用来保存前一个鼠标移入的元素 (最开始为第一个li中的a )
  let prevA = document.querySelector(".nav ul li:first-child a");
  oUl.addEventListener("mouseover", function (e) {
    // 获取鼠标移入到的元素是否是 a
    const target = e.target;
    if (target.tagName.toLowerCase() !== "a") return;
    // 如果是a元素,则获取a元素的宽、高、与 ul左边的距离
    const width = target.offsetWidth;
    const height = target.offsetHeight;
    const left = target.offsetLeft;
    // 动态设置 move的宽度,高度和 left值
    move.style.width = width + "px";
    move.style.height = height + 10 + "px";
    move.style.left = left + "px";
    // 当前a标签文字颜色变为白色,其它的全还原
    if (prevA) {
      console.log(prevA);
      prevA.style.color = "#000";
    }
    move.style.display = "block";
    target.style.color = "#fff";
    prevA = target;
  });
</script>
</body>
</html>