必须用可动画CSS属性(如max-height、opacity)分步控制列表项收缩与上移,因display:none或remove()等结构性DOM操作无法触发transition插值;max-height需设具体数值并配合overflow:hidden,transitionend须校验propertyName且用{once:true}避免误删。

不能靠 display: none 或直接 remove() 实现平滑收缩和上移——浏览器对结构性 DOM 操作不做插值,必须用可动画 CSS 属性分步控制。
为什么 transition 对 remove() 无效
调用 element.remove() 是 DOM 结构变更,不触发任何 CSS 属性变化,transition 根本没机会启动。即使给 li 写了 transition: all 0.3s,元素也会“瞬移消失”,周围项不会平滑上移——因为它们的重排(reflow)发生在动画帧之外,视觉上就是跳变。
-
display、visibility、height(设为auto)等属性本身不可动画或无法插值 - 真正能被浏览器插值的只有数值型属性:
max-height、opacity、transform - 列表项父容器若为
display: flex或grid,max-height动画可能完全不生效,需改用block布局
max-height + overflow: hidden 实现收缩动画
这是兼容性最好、最可控的方案。关键不是让高度“归零”,而是让浏览器能对一个明确的数值区间做插值。
-
max-height必须设具体值(如120px),不能用auto或fit-content - 初始状态和动画态都必须声明
overflow: hidden,否则内容会撑开容器,动画失效 - CSS 示例:
li {<br> max-height: 120px;<br> overflow: hidden;<br> transition: max-height 0.25s ease-out, opacity 0.25s ease-out;<br>}<br>li.removing {<br> max-height: 0;<br> opacity: 0;<br>} - JS 触发时:先加类
el.classList.add('removing'),不要立刻remove()
监听 transitionend 并精准移除元素
一个元素可能同时过渡多个属性,transitionend 会触发多次。不加过滤就删,大概率删早了(比如 opacity 先结束,max-height 还在动)。
立即学习“前端免费学习笔记(深入)”;
- 必须校验
e.propertyName === 'max-height',只在此时执行el.remove() - 用
{ once: true }避免重复绑定:el.addEventListener('transitionend', (e) => {<br> if (e.propertyName === 'max-height') {<br> el.remove();<br> }<br>}, { once: true }); - 如果列表项高度差异大,
max-height统一设为最大可能值(如200px),避免短项动画过慢 - 别在 CSS 里写
will-change: max-height—— 应在 JS 加类时动态设置el.style.willChange = 'max-height',删完立刻设回'auto'
View Transitions API 的位移补间是否值得用
它真能自动计算邻项上移路径,但现实约束极多,目前仅适合 Chrome/Edge 111+ 环境下的降级兜底。
- 必须为每个
li设置稳定唯一的data-view-transition-name,删除后新 DOM 里不能再复用该 name - 不能在
DOMContentLoaded中调用,得包裹在用户交互回调或requestAnimationFrame微任务中 - 父容器禁用
transform、position: absolute,否则坐标匹配失败,位移乱飞 - Firefox/Safari 完全不支持,且 UA 检测不可靠,必须运行时检测:
if ('startViewTransition' in document) - 实际项目中,
max-height方案仍是主力;View Transitions 更适合作为渐进增强层,而非主逻辑
真正难的不是写出动画,而是在不同高度项共存、滚动区域复杂、flex 布局嵌套、跨浏览器兼容等真实场景下,让每一帧都落在预期位置——max-height 值怎么估、overflow 何时生效、transitionend 怎么防误触,这些细节卡住的往往不是语法,而是渲染管线里的隐式行为。


















