1721 字
9 分钟

关于hover触发范围和transition的位置以及使用

欢迎来到我的博客!#

使用 Markdown 格式编写。

今天学到了什么#

  • 学了calc()函数和var变量的使用方法
image-20260102214952283

这是一个非常好的问题!这涉及到 HTML 结构(DOM 树)与 CSS 视觉渲染范围的区别。

简单来说:虽然视觉上 ul 超出了 .nav 的高度范围,但在代码结构上,ul 仍然是 .nav 的子元素。

以下是详细解释:

  1. DOM 结构决定父子关系: 请看你的 HTML 代码结构:

    <div class="nav">
    <!-- ... -->
    <ul>
    <!-- ... -->
    </ul>
    </div>

    <ul> 标签是包裹在 <div class="nav"> 标签内部的。在浏览器看来,ul.nav 的一部分(子元素)。

  2. :hover 的触发机制: 在 CSS 中,当鼠标悬停在子元素上时,也会被视为悬停在父元素上(事件冒泡)。 既然 ul.nav 的子元素,当你鼠标经过 ul(以及里面的 li)时,浏览器认为你也正在经过 .nav,所以 .nav:hover 样式会被触发。

  3. 视觉范围 vs 实际结构: 你在 CSS 中给 .nav 设置了固定高度:

    .nav {
    height: 50px; /* 限制了视觉高度 */
    /* overflow: hidden; <-- 如果你加了这行,溢出的 ul 就会看不见 */
    }

    虽然 .nav 只有 50px 高,而 ul 的内容溢出到了这个高度之外,但溢出的内容仍然属于这个父容器。除非你把 ul 移到 .nav 外面,或者改变 HTML 结构,否则鼠标碰到 ul 就等于碰到了 .nav

总结: 是因为 HTML 父子关系 决定的。只要 ul.nav 里面,鼠标摸到 ul 就等于摸到了 .nav,从而触发 .nav:hover

transform详解#

transition(过渡)是 CSS3 中非常核心的一个属性,它的作用是让元素从一种样式状态,平滑地改变为另一种样式状态

如果没有它,CSS 的状态变化(比如鼠标悬停)是瞬间完成的(生硬);有了它,就有了动画效果。

1. 语法公式#

通常我们使用简写属性,顺序如下:

/* 推荐顺序:谁做过渡 花多久 怎么运动 等多久再开始 */
transition: property duration timing-function delay;

2. 四个参数详解#

transition-property (过渡属性)#

  • 含义:指定哪个 CSS 属性要进行动画。
  • 常用值
    • width, background-color, opacity, transform 等具体属性。
    • all最常用,代表所有能变化的属性都一起过渡。
    • none:没有属性过渡。

transition-duration (持续时间) [必写]#

  • 含义:动画完成需要的时间。
  • 单位:秒(s) 或 毫秒(ms)。
  • 注意:如果不写这个,默认为 0,就没有动画效果。
  • 例子0.5s500ms

transition-timing-function (运动曲线)#

  • 含义:动画的速度变化(是匀速,还是先快后慢)。
  • 常用值
    • ease:默认值,慢速开始 -> 变快 -> 慢速结束(最自然)。
    • linear:匀速(适合旋转动画)。
    • ease-in:加速(慢 -> 快)。
    • ease-out:减速(快 -> 慢,你的代码里用的就是这个,适合入场动画)。
    • steps(n):分步执行(像帧动画一样一卡一卡的)。

transition-delay (延迟时间)#

  • 含义:触发后,等待多久才开始执行动画。
  • 例子1s(鼠标放上去,等1秒才开始动)。
  • 你的代码应用calc(var(--i) * 0.1s) 就是利用延迟来实现“一个接一个”出来的效果。

3. 进阶技巧:双向控制(你刚才遇到的问题)#

transition 写在不同位置,效果不同:

  • 写法 A:写在元素本身(通用写法)

    div {
    width: 100px;
    transition: all 0.5s; /* 去和回 都是 0.5s */
    }
    div:hover {
    width: 200px;
    }

    效果:鼠标移入变宽用 0.5s,鼠标移出恢复也用 0.5s。

  • 写法 B:分别写在元素和 中(精细控制)

    div {
    width: 100px;
    /* 1. 控制“失去 hover”时的过渡(收起/恢复) */
    transition: all 0.2s;
    }
    div:hover {
    width: 200px;
    /* 2. 控制“获得 hover”时的过渡(展开/触发) */
    transition: all 1s;
    }

    效果:鼠标移入(1s),鼠标移出(0.2s)。这就是为什么刚才我们可以分别设置展开和收起的时间。

4. 避坑指南#

  1. display: none 无法过渡:如果你想让元素从“消失”到“出现”,不能用 display: nonedisplay: block,因为浏览器无法计算这两个状态之间的中间值。
    • 解决方案:使用 opacity: 0 (透明) 和 visibility: hidden 配合 opacity: 1
  2. 高度 height: auto 无法过渡:从 0px 过渡到 auto 是无效的。
    • 解决方案:使用 max-height(从 0 到 1000px)或者用 JS 计算高度。

transition位置详解#

transition 写在元素本身(默认状态)还是写在 :hover 伪类中,决定了动画是“双向一致”还是“去回不同”。

1. 写在元素本身(推荐新手使用)#

这是最常见的写法。

  • 位置:写在 ul li { ... } 里。
  • 效果“去”和“回”都一样。鼠标放上去(展开)有动画,鼠标移开(收起)也有完全一样的动画。
  • 代码示例
    ul li {
    /* 无论变成什么样,只要属性变了,都用 0.3s 过渡 */
    transition: all 0.3s;
    }

2. 写在 :hover#

  • 位置:写在 .nav:hover ul li { ... } 里。
  • 效果只有“去”的时候有动画
    • 鼠标放上去:有动画(因为触发了 hover 里的 transition)。
    • 鼠标移开:瞬间恢复(因为回到了默认状态,而默认状态没有写 transition)。

3. 两边都写(高级控制)#

这就是你刚才遇到的情况,为了让“展开”慢一点(带延迟),“收起”快一点(无延迟)。

  • 默认状态 (ul li):控制收起/恢复时的动画。
  • Hover状态 (:hover):控制展开/触发时的动画。

总结建议#

  • 如果你想要简单的效果:直接写在 元素本身 (ul li) 上,用 transition: all 0.3s
  • 如果你想要精细控制(比如展开时一个接一个,收起时一起消失):需要两边都写,分别设置不同的 durationdelay

今日案例的完整代码展示

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
ul,
ol {
list-style: none;
}
body {
min-height: 100vh;
background: linear-gradient(to bottom, #577ad4, #ad3d88);
}
.nav {
width: 250px;
height: 50px;
background-color: #fff;
position: fixed;
top: 20px;
left: 20px;
z-index: 100;
line-height: 50px;
}
.nav .toggle {
width: 100%;
cursor: pointer;
color: #ff216d;
padding-left: 20px;
}
ul {
position: relative;
/* display: flex;
flex-direction: column; */
}
ul li {
height: 50px;
padding-left: 20px;
background-color: #fff;
color: #333;
cursor: pointer;
position: relative;
transform: translateX(-250px);
opacity: 0;
/* 1. 鼠标离开(收起)时的过渡:需要包含 transform 和 opacity */
/* 之前只写了 transform,所以 opacity 变成瞬间变化了 */
transition: transform 0.3s ease-out calc(var(--i) * 0.1s), opacity 0.3s ease-out calc(var(--i) * 0.1s), background-color 0.2s;
}
li:hover {
background-color: #ebe7e7;
}
.nav:hover ul li {
transform: translateX(0);
opacity: 1;
}
</style>
</head>
<body>
<div class="nav">
<!-- 菜单 -->
<div class="toggle">
菜单
</div>
<!-- 二级菜单 -->
<ul>
<li style="--i:0;">主页</li>
<li style="--i:1;">关于我们</li>
<li style="--i:2;">售后服务</li>
<li style="--i:3;">产品介绍</li>
<li style="--i:4;">联系我们</li>
</ul>
</div>
</body>
</html>

赞助支持

如果这篇文章对你有帮助,欢迎赞助支持!

赞助
关于hover触发范围和transition的位置以及使用
https://firefly.cuteleaf.cn/posts/study-everyday/2026-01-02今日学习-今日学习-关于transition/
作者
LJC
发布于
2026-01-02
许可协议
CC BY-NC-SA 4.0
最后更新于 2026-01-02,距今已过 2 天

部分内容可能已过时

目录