关于hover触发范围和transition的位置以及使用
欢迎来到我的博客!
使用 Markdown 格式编写。
今天学到了什么
- 学了calc()函数和var变量的使用方法

nav的范围不包括ul,但是鼠标经过ul的时候,还会触发 .nav 为什么?
这是一个非常好的问题!这涉及到 HTML 结构(DOM 树)与 CSS 视觉渲染范围的区别。
简单来说:虽然视觉上 ul 超出了 .nav 的高度范围,但在代码结构上,ul 仍然是 .nav 的子元素。
以下是详细解释:
-
DOM 结构决定父子关系: 请看你的 HTML 代码结构:
<div class="nav"><!-- ... --><ul><!-- ... --></ul></div><ul>标签是包裹在<div class="nav">标签内部的。在浏览器看来,ul是.nav的一部分(子元素)。 -
:hover的触发机制: 在 CSS 中,当鼠标悬停在子元素上时,也会被视为悬停在父元素上(事件冒泡)。 既然ul是.nav的子元素,当你鼠标经过ul(以及里面的li)时,浏览器认为你也正在经过.nav,所以.nav:hover样式会被触发。 -
视觉范围 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.5s或500ms。
③ 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. 避坑指南
display: none无法过渡:如果你想让元素从“消失”到“出现”,不能用display: none到display: block,因为浏览器无法计算这两个状态之间的中间值。- 解决方案:使用
opacity: 0(透明) 和visibility: hidden配合opacity: 1。
- 解决方案:使用
- 高度
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。 - 如果你想要精细控制(比如展开时一个接一个,收起时一起消失):需要两边都写,分别设置不同的
duration或delay。
今日案例的完整代码展示
<!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>赞助支持
如果这篇文章对你有帮助,欢迎赞助支持!
部分内容可能已过时