[Bug Report][3.3.21] v-tabs toggle auto-scroll to top
Environment
Vuetify Version: 3.3.21 Vue Version: 3.3.4 Browsers: Chrome 117.0.0.0 OS: Windows 10
Steps to reproduce
Scroll down to see the tab content, switch tabs to the right, and the page will automatically scroll to the top
Expected Behavior
By default, when switching tabs, the page will not scroll
Actual Behavior
Switch the tab to the right and the page will automatically scroll to the top
Reproduction Link
#17046
#7108 So the problem may be with the v-window-item component and there has been a fix Using :transition="false" on v-window-item can prevent scrolling, but it is not perfect, especially when switching from the first tab to the third tab, it will still scroll
I see the same thing in Firefox and it makes v-tabs unusable IMO. The :transition="false" workaround mentioned above is very unstable. I've also seen it suggested to set a fixed height on the v-tabs component but this has no effect for me.
I see the same thing in Firefox and it makes v-tabs unusable IMO. The
:transition="false"workaround mentioned above is very unstable. I've also seen it suggested to set a fixed height on thev-tabscomponent but this has no effect for me.
You need to put fixed height on the v-window component:
<v-tabs v-model="tab" background-color="transparent" color="primary">
<v-tab value="one">One</v-tab>
<v-tab value="two">Two</v-tab>
</v-tabs>
<v-divider></v-divider>
<v-window v-model="tab" class="mb-6" style="height: 500px">
<v-window-item value="one">
hello
</v-window-item>
<v-window-item value="two">
there
</v-window-item>
</v-window>
EDIT: Actually even min-height is working so it is a good workaround
The problem still happens in Vuetify 3.6.3. The workaround using fixed height is not satisfactory for a reactive layout, and the min-height only works if it is set to the real height.
Edit: I had to implement a dirty workaround, using a ref function and a ResizeObserver.
<div :ref="onRefParentElement">
<v-tabs v-model="tab">
<v-tab v-for="(item, index) in items" :value="index">
ITEM {{ index }}
</v-tab>
</v-tabs>
<v-window v-model="tab" :style="`min-height: ${minHeight}px`">
<v-window-item v-for="(item, index) in items" :value="index">
Content for item {{ index }}
</v-window-item>
</v-window>
</div>
// the value used to set min-height in v-window style
const minHeight = ref(0)
// observed is used to keep track of the element set in the ResizeObserver
let observed = null
const observer = new ResizeObserver(entries => {
for (const entry of entries) {
// set the min-height of v-window to the height of the v-window__container
minHeight.value = entry.contentRect.height
}
})
// this function will be triggered every time the parent element changes, thanks to the ref in the template.
// When the height of its children changes, the parent height also changes and this function is called.
function onRefParentElement(el) {
if (observed) {
// first, we remove any existing observed element
observer.unobserve(observed)
observed = null
}
if (el) {
// then, if our component is displayed, we get the inner container of the v-window and add it in the ResizeObserver.
// Thus when the v-window content changes, the oberver callback will be triggered
const vWindow = el.querySelector(".v-window__container")
observer.observe(vWindow)
observed = vWindow
}
}
@vdechef
I did not have success with your workaround in my code and I don't understand why. First of, I did try it within the VPlay sandbox linked previously and it worked there. Note also that I do NOT have problems if I use standard VTabs within a single vue page - where the tabs are part of the page already. I do have problems when my ROUTES change upon changing tabs and I wonder if there is something there that is preventing your solution from working. Initially the VPlay sandbox had a similar failure signature as my implementation where switching tabs switched routes so I was optimistic your implementation would fix mine as well - since the VPlay worked. But, while the observer gets triggered and it displays different minHeights for the different tabs, I still get the jumping to the top of the page.
I feel the only solution right now is to forgo making each tab its own route and hence store all the data in the main vue page - since that seems to be working for me without even needing your workaround.
Obviously without giving you code it might be difficult to understand what I am doing, or how to fix it, but I hope this issue does not get closed since this is a problem. I am using Vue 3.4.27 and Vuetify 3.6.7
tested with Version: 3.6.14
Hi there,
I did some investigation because I had the same issue. The problem is the dynamic height css property which is set on the v-window__container element. I have not yet debugged the vuetify code to see where exactly this is done but maybe somebody may take a shot on that meanwhile.
To reproduce:
<template>
<v-tabs v-model="activeTab">
<v-tab value="tab1">Tab 1</v-tab>
<v-tab value="tab2">Tab 2</v-tab>
</v-tabs>
<v-tabs-window v-model="activeTab" class="custom-window">
<v-tabs-window-item value="tab1">
<div class="d-flex w-100" style="height: 500px; background: yellow"></div>
</v-tabs-window-item>
<v-tabs-window-item value="tab2">
<div class="d-flex w-100" style="height: 500px; background: blue"></div>
</v-tabs-window-item>
</v-tabs-window>
</template>
<script lang="ts">
export default defineComponent({
name: 'test',
data(): Data {
return {
activeTab: 'tab1',
};
},
});
</script>
<style>
.custom-window .v-window__container {
min-height: 500px;
}
</style>
- you will see once the
v-window__containerhas a min height set, the scrolling is gone. - However while writing I added some min height to custom-window and changed it in the browser console. on tab change it got reverted. this might also be an indicator, that the component is re-rendered and this is the root of all evil.
Possible quick fix:
Add the min height as above. But there has to be an issue in the core code.