How to Create an infinite scroll with VueJs

How to Create an infinite scroll with VueJs

We've all been found guilty of spending a longer time than intended scrolling through our social media feeds. I mean, we keep entertaining that curiosity of what we'll find by simply sliding our fingers along the screen for one more time. The whole point is that users are persuaded to remain on your application when they experience unending content. So, if you're building an app that loads a huge amount of data (could be images, short videos, stories, or anything) and you'll need to lure users to remain, then giving your application a sense of infinite scroll is something to go for.

For this article, we'll create an infinite scroll with VueJs. All you'll need is

  • A basic understanding of Vue and

  • Axios installed locally

We'll be consuming API data from opentdb, (it's free and you don't need an authentication key) to display random questions and their answers. Here's a link to the full project on codesandbox

Step 1: Firstly, we'll fetch and house the API data that will be initially displayed on the app. To do this, we’ll create a data property that will eventually house our information, then we’ll retrieve the data by defining a function in the methods object which we'll call in the mounted lifecycle hook.

App.vue

data() {
    return {
      questions: []
    }
  },
  methods: {
    getQuestions() {
      axios
        .get("https://opentdb.com/api.php?amount=5")
        .then((response) => this.questions.push(...response.data.results))
        .catch((error) => console.log(error))
    },

mounted() {
    this.getQuestions()
  }

In this block, we simply sent a get request to the API's endpoint, specifying we want to receive 5 objects per call. In this case, each object contains a question. Feel free to increase or reduce the objects per call by altering amount=5. If you take a little time to explore the information in the api's response, you'll see that our priced information lives in response.data.results. Hence we push response.data.results unto the questions data property we declared earlier.

Note: Adding ... allows us to push the content of response.data.results individually and not the whole of response.data.results which is an object containing other objects. For this reason, we had to declare our questions data property as an empty array instead of a null value.

Step 2: We'll display the initial data retrieved from the API. To achieve this, we create a component to display each question in the questions array as a card. Let's name the component Card.

App.vue

<template>
  <div id="app">
    <h2>Random questions and their answers</h2>
    <div v-for="(question, index) in questions" :key="index">
      <Card :question="question" />
    </div>
  </div>
</template>

In this block, we loop through each object in the questions array as question, also retrieving its index in the array as index which we'll use as a v-bind key. Each question is then bound to our Card component as a prop called question.

Card.vue

<template>
  <div class="card">
    <small class="card-header"
category: {{ question.category }}
      <span>difficulty: {{ question.difficulty }}</span></small>
    <p>{{ computedQuestion }}</p>
    <small class="card-answer">
       answer: <span>{{ question.correct_answer }}</span></small>
  </div>
</template>

So we head over to our Card component to render the received question. For this article, We'll render just a few of the properties available in each question. Feel free to add more, make yours more interesting!

Note: The computedQuestion rendered was written as acomputed property to replace the special entities contained in our API's questions with their corresponding character.

On our browser, we'll get - scroll.png Notice the length of the scroll while it displays just five questions.

Step 4: The moment we've all been waiting for! Our infinite scroll will work with a very easy logic - detect if the user has scrolled to the bottom of the window, and if they have, fire a function that'll retrieve more data.

App.vue

getMoreQuestions() {
      window.onscroll = () => {
        let bottomOfWindow =
          document.documentElement.scrollTop + window.innerHeight ===
          document.documentElement.offsetHeight;

        if (bottomOfWindow) {
          this.getQuestions()
        }
      }
    }
  • document.documentElement.scrollTop measures the distance between the element's top and its topmost visible content
  • window.innerHeight is a property of the Window interface that returns the interior height of the window in pixels, including the height of the horizontal scroll bar, if present.
  • document.documentElement.offsetHeight property returns the height of an element, including vertical padding and borders, as an integer.

By Implication, the point at which document.documentElement.offsetHeight equals the sum of document.documentElement.scrollTop and window.innerHeight is the bottom of the window. Using if statement, we fire getQuestions as a call back function. Lastly, we call this function (getMoreQuestions) in the mounted lifecycle hook.

App.vue

 mounted() {
    this.getQuestions()
    this.getMoreQuestions()
  }

Now we get this on our browser -

scroll2.png Notice the length of the scroll, and compare it to what we had initially.

Just like that! Now, all you've got to do is keep scrolling, maybe you'll find the end of the page, who knows?

I'll be more than willing to attend to any question you may have, just drop by the comment section, thanks.