vuetify icon indicating copy to clipboard operation
vuetify copied to clipboard

[Bug Report][3.3.21] v-tabs toggle auto-scroll to top

Open Sweet520 opened this issue 2 years ago • 7 comments

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

https://play.vuetifyjs.com/#...

Sweet520 avatar Oct 11 '23 19:10 Sweet520

#17046

Sweet520 avatar Oct 12 '23 07:10 Sweet520

#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

Sweet520 avatar Oct 12 '23 15:10 Sweet520

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.

dixhuit avatar Dec 02 '23 22:12 dixhuit

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.

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

fquellec avatar Dec 13 '23 14:12 fquellec

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 avatar May 14 '24 09:05 vdechef

@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

Z-Knight avatar May 28 '24 05:05 Z-Knight

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__container has 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.

notsure avatar Aug 21 '24 08:08 notsure