Home > Back-end >  Scroll to the top of the list in vue scrollable list (v-auto-complete)
Scroll to the top of the list in vue scrollable list (v-auto-complete)

Time:01-01

I have a scrollable list that i can select multiple items from. I want to programmatically implement a way to scroll to top of the list every time i change the search input.

Desired behavior: For example; when i type 'b' in the search list, and scroll down, then if i type 'e' again i would like it to scroll to the very top

Here's a code example showing what i would like to do, I've tried two different methods. I have tried two different approaches in the code below where i utilize scrollTop feature of the html Element and goTo function from vuetify but none of them worked.

Code demo-> https://www.codeply.com/p/ZGBNEHW7YS

In case the demo expires, the actual code;

<div id="app">
    <v-app>
        <v-main >
            <h1 >AutoComplete Infinite Scroll Example</h1>
            <v-autocomplete 
                id="autoComplete"
                ref="autoComplete"
                v-model="selected"
                :items="beers" 
                item-text="name" 
                item-value="id" 
                :search-input.sync="search"
                label="Search the beers.."
                return-object
                multiple
                autocomplete="off"
                >
                <template v-slot:append-item>
                  <div v-intersect="onIntersect" >
                     Loading more items ...
                  </div>
                </template>
            </v-autocomplete>
      </v-main>
    </v-app>

new Vue({
    el: '#app',
    vuetify: new Vuetify(),
    data() {
        return {
          search:'',
          beers: [],
          selected: null,
          perPage: 30,
          page: 1,
        }
    },
    methods: {
        getBeers() {
            console.log('page', this.page)
            const apiUrl = `https://api.punkapi.com/v2/beers?page=${this.page}&per_page=${this.perPage}`
            axios.get(apiUrl)
                .then(response => {
                  this.beers = [
                      ...this.beers,
                      ...response.data
                  ]
            })
        },
        onIntersect () {
            console.log('load more...')
            this.page  = 1
            this.getBeers()
        },
    },
    watch:{
        search:function(){
         //first method
          let myId=document.getElementById('autoComplete')
          if(myId){
            myId.scrollTop = 0
    
          }
          
          //second method with ref  and vuetify goto
          this.$nextTick(() => {
            this.$vuetify.goTo(this.$refs.autoComplete) 
          })
        }
    },
    created() {
        this.getBeers()
    }
})

CodePudding user response:

You need something like this (see demo in full page)

new Vue({
  el: '#app',
  vuetify: new Vuetify(),
 data: () => ({
    beers: [],
          selected: null,
          perPage: 30,
          page: 1,
  
  }),
     created() {
        this.getBeers()
    },
  methods: {
    goToTop(){
      this.beers = [];
      this.page = 1;
      this.getBeers();
    },
       getBeers() {
            console.log('page', this.page)
            const apiUrl = `https://api.punkapi.com/v2/beers?page=${this.page}&per_page=${this.perPage}`
            axios.get(apiUrl)
                .then(response => {
                  this.beers = [
                      ...this.beers,
                      ...response.data
                  ]
            })
        },
            onIntersect () {
            this.page  = 1
            this.getBeers()
        },
  },
})
.scroll-to-top {
  position:sticky;
  bottom:8px;
  width:100%;
  display:flex;
  justify-content :right;
  background-color: rgba(0, 0, 0, 0); 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.24.0/axios.min.js"></script>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/@mdi/[email protected]/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.js"></script>


 <div id="app">
  <v-app id="inspire">
    <v-card>
      <v-container fluid>
        <v-row
          align="center"
        >
          <v-col cols="12">
            <v-autocomplete 
                v-model="selected"
                :items="beers" 
                item-text="name" 
                item-value="id" 
                label="Search da beers.."
                return-object
                autocomplete="off"
                >
              
          
               <template v-slot:append-item>
                 <div v-intersect="onIntersect" >
                     Loading more items ...
                  </div>
                            <div >
                              <v-btn
                                color="primary"
                                small
                                
                                @click="goToTop"
                              >
                                 Go to top
                              </v-btn>
                            </div>
                          </template>
            </v-autocomplete>
          </v-col>
        </v-row>
      </v-container>
    </v-card>
  </v-app>
</div>

CodePudding user response:

Here's how i solved by using zergski's example.

Casting was required because of typescript.

scrollTop(): void {
  let scrollList = document.querySelector('.v-autocomplete__content')

  let firstElement = document.querySelector('.company-item-name') as HTMLElement

  if (scrollList && firstElement && firstElement.offsetTop) {
    scrollList.scrollTop = firstElement.offsetTop
  }
},
  • Related