Бесконечная прокрутка
Vs Vue3 Select не имеет функционала бесконечной прокрутки списка, но ее можно реализовать, при помощи событий open
, close
и search
, параметра filterable
и слота list-footer
.
Разберем пример приведенный ниже. Начнем с data
:
observer
- новый объектIntersectionObserver
сinfiniteScroll
установленной в качестве функции обратного вызоваlimit
- количество отображаемых опцийsearch
- встроенную фильтрацию компонента отключили через параметр, поэтому необходимо хранить строку поиска
Когда откроется выпадающий список, будет выдано событие open
и будет вызван onOpen
. Мы ждем $nextTick()
, чтобы нужный нам $ref
существовал, затем начинаем наблюдать за ним на предмет пересечения.
Наблюдатель настроен на вызов InfiniteScroll
, когда <li>
полностью виден в списке. Здесь выполняется некоторая деструктуризация, чтобы получить первый наблюдаемый элемент и, в частности, свойства isIntersecting и target. Если <li>
пересекается, мы увеличиваем ограничение и обеспечиваем, чтобы положение прокрутки оставалось там, где оно было до изменения размера списка. Опять же, здесь важно дождаться $nextTick, чтобы элементы DOM были вставлены перед установкой положения прокрутки.
Вы могли бы создать observer непосредственно в data(), но поскольку рендер страницы может на стороне сервера, IntersectionObserver не существует в этой среде, поэтому нам нужно сделать это в mounted()
.
<template>
<v-select
:options="paginated"
:filterable="false"
@open="onOpen"
@close="onClose"
@search="(query) => (search = query)"
>
<template #list-footer>
<li v-show="hasNextPage" ref="load" class="loader">
Loading more options...
</li>
</template>
</v-select>
</template>
<script>
import countries from '../data/countries'
export default {
name: 'InfiniteScroll',
data: () => ({
observer: null,
limit: 10,
search: '',
}),
computed: {
filtered() {
return countries.filter((country) => country.includes(this.search))
},
paginated() {
return this.filtered.slice(0, this.limit)
},
hasNextPage() {
return this.paginated.length < this.filtered.length
},
},
mounted() {
this.observer = new IntersectionObserver(this.infiniteScroll)
},
methods: {
async onOpen() {
if (this.hasNextPage) {
await this.$nextTick()
this.observer.observe(this.$refs.load)
}
},
onClose() {
this.observer.disconnect()
},
async infiniteScroll([{isIntersecting, target}]) {
if (isIntersecting) {
const ul = target.offsetParent
const scrollTop = target.offsetParent.scrollTop
this.limit += 10
await this.$nextTick()
ul.scrollTop = scrollTop
}
},
},
}
</script>
<style scoped>
.loader {
text-align: center;
color: #bbbbbb;
}
</style>