Vue Transition Group过渡时v-for对象最后一项溢出容器问题
问题背景
在对feedbacksToShow对象应用**组过渡(group transition)**效果时,仅v-for渲染的列表最后一项执行过渡时会溢出#feedback-list div容器,其余功能均运行正常。
由于项目中使用了tooltip(文字提示)组件,悬停反馈项时提示框需要溢出容器展示,因此无法为外层容器div设置overflow-hidden样式。
相关实现代码如下:
业务侧调用代码:
<FadeTransition group class="tw-relative tw-hidden lg:tw-block tw-text-xs tw-text-light-grey-blue tw-font-semibold tw-mt-1" id="feedback-list"> <div v-for="feedback in feedbacksToShow" :key="feedback[0].feedbackable_type" class="tw-w-full tw-flex lg:tw-justify-between tw-gap-x-2 tw-mt-2" > <span>{{ getCostTypeTitle(feedback[0]["feedbackable_type"]) }}</span> <span class="tw-text-yellow-400 hover:tw-text-yellow-500"> See {{ getCostTypeFeedbackCount(feedback) }} </span> </div> </FadeTransition>
封装的通用过渡组件代码:
<template> <component :is="group ? 'transition-group' : 'transition'" :tag="tag" name="fade" v-bind="$attrs" v-on="hooks" > <slot></slot> </component> </template> <script> import { Transition, TransitionGroup } from 'vue'; export default { components: { Transition, TransitionGroup, }, props: { group: { type: Boolean, default: false }, tag: { type: String, default: "div" } }, computed: { hooks() { return { leave: this.setAbsolutePosition } } }, methods: { setAbsolutePosition(el) { if (this.group) { el.style.position = "absolute"; } } } }; </script> <style> .fade-move, .fade-enter-active, .fade-leave-active { transition: all 0.4s ease-in-out; } .fade-enter-from, .fade-leave-to { opacity: 0; } </style>
注:原业务代码中v-for循环的div元素class属性多写了一个前置单引号,会导致Tailwind样式失效,使用前需要先修正这个写法问题。
问题根因
溢出问题来自现有离开过渡的逻辑缺陷:
- 给进入离开阶段的元素设置
position: absolute时,没有同步设置匹配当前视觉位置的定位坐标,也没有处理容器高度的动态适配。 - 非最后一项执行离开过渡时,下方的列表项会通过move过渡向上移动,刚好占住离开项脱离文档流后空出的位置,绝对定位的离开项视觉上和其他元素位置重叠,不会表现出溢出。
- 最后一项执行离开过渡时,没有下方元素上移占位,绝对定位的离开项脱离文档流后,父容器高度会随内容减少自动收缩,但离开项因为没有设置明确的top偏移,会停留在原位置,超出收缩后的容器高度边界,形成溢出。
修复方案
不需要给容器添加overflow: hidden,只需要补全离开过渡的定位逻辑、增加过渡结束后的样式清理即可,不会影响tooltip的正常溢出展示:
<script> import { Transition, TransitionGroup } from 'vue'; export default { components: { Transition, TransitionGroup, }, props: { group: { type: Boolean, default: false }, tag: { type: String, default: "div" } }, computed: { hooks() { return { leave: this.setAbsolutePosition, afterLeave: this.clearInlineStyle } } }, methods: { setAbsolutePosition(el) { if (!this.group) return const elRect = el.getBoundingClientRect() const parentRect = el.parentElement.getBoundingClientRect() // 给离开元素设置精准定位,固定在当前视觉位置 el.style.position = 'absolute' el.style.top = `${elRect.top - parentRect.top}px` el.style.left = `${elRect.left - parentRect.left}px` el.style.width = `${elRect.width}px` // 给父容器临时设置最小高度,避免内容塌陷导致的定位错位 el.parentElement.style.minHeight = `${parentRect.height}px` }, clearInlineStyle(el) { if (!this.group) return // 过渡结束后清理所有临时内联样式 el.style.position = '' el.style.top = '' el.style.left = '' el.style.width = '' el.parentElement.style.minHeight = '' } } }; </script>
修复后,最后一项离开时会被固定在原位置做透明度渐变,父容器高度会平滑过渡到最终高度,不会出现元素溢出容器的问题,tooltip组件也可以正常超出容器展示。
内容的提问来源于stack exchange,提问作者MW7
相关产品推荐
相关产品推荐

