Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infinite scroll not working #1685

Closed
7 tasks done
francoism90 opened this issue Jun 12, 2022 · 12 comments
Closed
7 tasks done

Infinite scroll not working #1685

francoism90 opened this issue Jun 12, 2022 · 12 comments

Comments

@francoism90
Copy link
Contributor

Describe the bug

I'm trying to use infinite scroll, however the provided examples don't seem to work.

<template>
  <div
    class="flex flex-col gap-2 p-4 w-300px h-300px m-auto overflow-y-scroll bg-gray-500/5 rounded"
    ref="el"
  >
    <div v-for="item in data" :key="item">
      {{ item }}
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref } from "vue";
import { useInfiniteScroll } from "@vueuse/core";

const el = ref<HTMLElement | null>(null);

useInfiniteScroll(el, () => console.log("fetch"), { distance: 100 }); // nothing logged to console
</script>

Reproduction

https://stackblitz.com/edit/vitejs-vite-srqqu3

System Info

System:
    OS: Linux 5.18 Arch Linux
    CPU: (12) x64 AMD Ryzen 5 3600 6-Core Processor
    Memory: 19.16 GB / 31.27 GB
    Container: Yes
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 18.3.0 - /usr/bin/node
    Yarn: 1.22.19 - /usr/bin/yarn
    npm: 8.5.5 - /usr/bin/npm
  npmPackages:
    @vueuse/components: ^8.6.0 => 8.6.0 
    @vueuse/core: ^8.6.0 => 8.6.0 
    vue: ^3.2.36 => 3.2.36

Used Package Manager

npm

Validations

@webfansplz
Copy link
Member

You should ensure the scroll container height < scroll content height.

@francoism90
Copy link
Contributor Author

@webfansplz Thanks for your reply. :)

Yeah, but still it doesn't seem to work. I'm just using the example(s), but nothing gets triggered.

@webfansplz
Copy link
Member

Can you provide a mini repo ,thanks

@murongg
Copy link
Contributor

murongg commented Jun 13, 2022

I tried no problem, this is my code:

<script setup lang="ts">
import { ref, toRefs } from 'vue'
import { useInfiniteScroll } from '@vueuse/core'

const el = ref<HTMLElement>(null)
const data = ref([1,2,3,4,5,6])

useInfiniteScroll(
  el,
  () => {
    for(let i = 0; i<=10;i++) {
      data.value.push(Math.random())
    }
  },
  { distance: 10 }
)
</script>

<template>
  <div ref="el" style="height: 100px;overflow: scroll;">
    <div v-for="item in data">
      {{ item }}
    </div>
  </div>
</template>

@francoism90
Copy link
Contributor Author

francoism90 commented Jun 13, 2022

@murongg Thanks, I'll try to add style="height: 100px;overflow: scroll;". I'm now using the given example on a clean page on Vue3 + tailwindcss.

@webfansplz I'll try to add a mini repo later this week, ty.

@francoism90
Copy link
Contributor Author

francoism90 commented Jun 15, 2022

@murongg @webfansplz I can confirm it works on Tailwind using style="height: 100px;overflow: scroll;" on the root element.

The root cause is h-300px, if I change this to anything smaller, it works because there are elements hidden in the scrollbar.

Would it be possible to force an infinite scroll, like let's say a Reddit/YT feed (focussed on the body instead)?

Thanks.

@lrstanley
Copy link

I would think that infinite scroll should at least fire once, even if the content has less height than the scroll container (IMO it being a configuration option would be best). In my case, just ensuring the parent is smaller doesn't really work well with my design. On desktop, there are no issues as the design has a small height and is center on the screen, but when I switch to mobile, the container in question stretches the screen, and thus if I don't load enough data in the first go, I can't load additional data.

I could just fetch more data when on mobile but that seems... counter intuitive.

@lrstanley
Copy link

My hacky solution for now is once I fetch data, I check if the container still isn't scrollable, it'll make another call (assuming there is still more data to fetch), e.g:

    // if this fetch was triggered by a scroll event, don't trigger it a second time.
    if (wasScrollEvent) return
    setTimeout(() => {
      if (scrollContainer.value.scrollHeight <= scrollContainer.value.clientHeight) {
        fetchEvents(true)
      }
    }, 500)

Full logic:

const scrollContainer = ref(null) // attached to infinite scroll container.

function fetchEvents(wasScrollEvent) {
  if (!hasNextPage.value) return

  events.executeQuery().then((result) => {
    const data = result.data.value.githubevents

    if (data.pageInfo.hasNextPage) {
      cursor.value = data.pageInfo.endCursor
    } else {
      hasNextPage.value = false
    }

    fetched.value = [...fetched.value, ...data.edges.map(({ node }) => toRaw(node))]

    // if this fetch was triggered by a scroll event, don't trigger it a second time.
    if (wasScrollEvent) return
    setTimeout(() => {
      if (scrollContainer.value.scrollHeight <= scrollContainer.value.clientHeight) {
        fetchEvents(true)
      }
    }, 500)
  })
}

onMounted(() => {
  fetchEvents()
})

@francoism90
Copy link
Contributor Author

francoism90 commented Jun 30, 2022

I've come up with my own solution, using VueScroll:

const { arrivedState } = useScroll(window, { throttle: 100, offset: { .. } });

watch(
  () => arrivedState.bottom,
  () => get(),
});

@adgower
Copy link

adgower commented Jul 15, 2022

I accomplished this using 'useIntersectionObserver'

<script setup>
import { ref } from 'vue';
import { useIntersectionObserver } from '@vueuse/core';

const el = ref(null);
const data = ref([1, 2, 3, 4, 5, 6]);

useIntersectionObserver(
  el,
  () => {
    const length = data.value.length + 1
    data.value.push(...Array.from({ length: 5 }, (_, i) => length + i))
  },
  {
    threshold: 0.5,
  }
)
</script>

<template>
  <a-row :gutter="[16, 16]">
    <a-col :span="24" v-for="item in data" :key="item">
      <a-card title="Card title">
        <p>card content {{ item }}</p>
      </a-card>
    </a-col>
  </a-row>
  <span ref="el"></span>
</template>

@dosstx
Copy link

dosstx commented Mar 22, 2023

@adgower Do you mind telling me how I could get your example code to work in Nuxt3 with the following logic? For some reaason it's not loading the products correctly when using fetch:

<script setup lang="ts">
// import { useInfiniteScroll } from '@vueuse/core'
import { useIntersectionObserver } from '@vueuse/core'

const el = ref(null)
const data = ref([])

useIntersectionObserver(
  el,
  async () => {
    const length = data.value.length + 1
    const res = await $fetch(
      `https://dummyjson.com/products/?limit=10&skip=${length}`
    )
    data.value.push(...res.products)
  },
  // options
  {
    threshold: 0.5
  }
)
</script>

<template>
  <div class="el">
    <div v-for="item in data" class="card" :key="item.id">
      <img :src="item.thumbnail" alt="" />
      <h3>{{ item.id }} - {{ item.title }}</h3>
      <p>{{ item.description }}</p>
    </div>
    <span ref="el" class="el"></span>
  </div>
</template>

<style>
html,
body,
#app {
  height: 100%;
}
.el {
  height: 100%;
  overflow-y: scroll;
}
.card {
  margin: 30px 10px;
  box-shadow: 0 0 3px 4px rgba(0, 0, 0, 0.1);
  border-radius: 10px;
  padding: 20px;
}
img {
  border-radius: 10px;
  display: block;
  width: 100%;
}
</style>

@graverat
Copy link

Thanks @adgower.
This code is not fully working on my side.
A first loading occurs whereas the intersection was not reached

Below is a change that fixes this unexpected behavior.
The condition checks the container is reached (seems rare as this is the goal of the useIntersectionObserver function :))

FYI @dosstx

useIntersectionObserver(
 el,
 ([{ isIntersecting }])  => {
   if ( isIntersecting) {
       const length = data.value.length + 1
       data.value.push(...Array.from({ length: 5 }, (_, i) => length + i))
   }
 },
 {
   threshold: 0.5,
 }
)

Resources:
https://github.com/vueuse/vueuse/blob/main/packages/core/useIntersectionObserver/demo.vue
https://dev.to/michaelsynan/reveal-on-scroll-with-nuxt-and-intersection-observer-api-53di

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants