CSS transition 必须写在起始状态且属性值需为可计算数值变化,如max-height代替height:auto;JS操作需避免同步读取offsetHeight等触发重排,应用requestAnimationFrame确保样式计算完成。

CSS transition 完全不依赖 :hover,只要元素的可过渡属性发生了变化(无论通过类名切换、JS 修改样式、内联 style 更新,甚至表单状态伪类),且满足触发条件,就能生效。
为什么加了 transition 却没动?常见触发失败原因
根本不是“只能 hover”,而是很多人漏掉了关键前提:
-
transition必须写在「起始状态」上(比如默认类或基础选择器),而不是只写在 hover 或目标类里 - 目标属性必须是可过渡的(如
color、opacity、transform、width等;display、height(无明确数值时)、font-size(某些旧浏览器)可能不生效或行为异常) - 属性值必须是「可计算的数值变化」:从
0px→100px可以,但从none→block或auto→200px不行 - 类名切换后,浏览器需要完成一次样式计算+布局+绘制流程;如果 JS 中同步修改 class 后立刻读取 offsetHeight 等,可能强制触发重排,打断过渡(需用
setTimeout或requestAnimationFrame分离)
用 class 切换触发 transition 的标准写法
核心是:基础样式定义初始态 + 过渡声明,目标类定义终态。例如实现按钮点击展开面板:
.panel {
max-height: 0;
opacity: 0;
overflow: hidden;
transition: max-height 0.3s ease-in-out, opacity 0.2s ease;
}
.panel.is-open {
max-height: 500px; / 避免用 auto /
opacity: 1;
}
注意点:
立即学习“前端免费学习笔记(深入)”;
-
max-height代替height: auto是常用 hack,确保有确定数值变化 - 多个属性过渡用逗号分隔,可分别指定时长/缓动,但必须都在起始选择器中声明
- 不要在
.is-open里重复写transition,否则会覆盖基础过渡设置
JS 控制类名时的典型陷阱与修复
直接 el.classList.toggle('is-open') 通常没问题,但以下情况会失效:
- 切换类后立即调用
el.offsetHeight—— 浏览器来不及计算新样式,仍读取旧值,导致过渡被跳过 - 用
display: none隐藏元素再添加类 ——display: none会彻底移除渲染,后续加display: block属于「重新插入」,不触发过渡 - 动画中途再次切换类 —— 可能中断当前过渡,重置为新起点(可用
transition-behavior: allow-discrete(实验性)缓解,但兼容性差)
安全做法示例:
button.addEventListener('click', () => {
panel.classList.toggle('is-open');
// 确保过渡触发后再读取尺寸(如需)
requestAnimationFrame(() => {
console.log(panel.scrollHeight);
});
});真正难的不是“怎么写 transition”,而是判断哪些属性能动、何时算“有效变化”、以及 JS 介入时如何不破坏渲染流水线。这些细节不处理好,哪怕语法全对,动画也静止不动。










