From 355b89bda2de2d2b473f904febe41950a703b439 Mon Sep 17 00:00:00 2001 From: waterWang Date: Mon, 3 Aug 2026 13:51:48 +0800 Subject: [PATCH] fix(VVirtualScroll): preserve measured heights when items change to prevent scroll jump When the items array changes (e.g. an item is removed via splice), the sizes and offsets arrays were completely reset, causing all previously measured heights to be lost. The offsets would then be recalculated using the default estimate (16px), which is far from the actual measured heights, causing a visible scroll position jump. Fix: preserve existing height measurements for items that remain at the same index after the items array changes. This keeps the offsets largely accurate and prevents the scroll position from jumping. Closes #22610 --- packages/vuetify/src/composables/virtual.ts | 23 ++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/packages/vuetify/src/composables/virtual.ts b/packages/vuetify/src/composables/virtual.ts index 72f8da07ded..b741cb24284 100644 --- a/packages/vuetify/src/composables/virtual.ts +++ b/packages/vuetify/src/composables/virtual.ts @@ -291,9 +291,26 @@ export function useVirtual (props: VirtualProps, items: Ref) { }) }) - watch(items, () => { - sizes = Array.from({ length: items.value.length }) - offsets = Array.from({ length: items.value.length }) + watch(items, (newItems, oldItems) => { + const newLength = newItems?.length ?? 0 + const oldLength = oldItems?.length ?? 0 + const oldSizes = sizes.slice() + + sizes = Array.from({ length: newLength }) + offsets = Array.from({ length: newLength }) + + // Preserve existing height measurements for items that remain + // at the same index, so scroll position doesn't jump when items + // are added or removed from the list. + if (oldLength > 0 && newLength > 0) { + const minLength = Math.min(oldLength, newLength) + for (let i = 0; i < minLength; i++) { + if (oldSizes[i]) { + sizes[i] = oldSizes[i] + } + } + } + updateOffsets.immediate() calculateVisibleItems() }, { deep: 1 })