This tutorial is out of date and no longer maintained.
Infinite Scroll is a UX pattern that suggests showing users few contents on page or app load. More contents are then loaded once the user starts scrolling down the page. These contents are loaded asynchronously by making a request to the server responsible for providing the content.
In this post, I’m going to talk about asynchronous operations in JavaScript, as well as how Vue ‒ an awesome JavaScript framework, can be used in this context. In the process, we will see a simple page that uses infinite scroll.
In programs written synchronously, here’s the deal ‒ you’ve got two lines of codes, L1 and L2. L2 cannot execute until L1 has finished running. check out the most basic example of a synchronous written program below:
console.log("ping");
console.log("response");
console.log("pong");
Outputping
response
pong
The problem with writing code this way is that it makes your app’s UX rigid and awkward. Let’s say you visit a shopping website, if you’re not running an ad blocker chances are you’ll be flooded with ads. If the e-commerce site was running on synchronously written code, your page would freeze up repeatedly while all of the images loaded for the pages and ads and you wouldn’t be able to click on anything until after the page has finished loading. If the page loaded a lot in the background it could take a while before you could interact with the page which would be very inconvenient.
Enter asynchronous programs, they offer us a much better deal, I think. You’ve got two lines of code, L1 and L2, where L1 schedules some task to be run in the future but L2 runs before that task completes. Check out this basic demo of writing asynchronous code:
console.log "ping"
$http.get("our_url", (data) => console.log "response")
console.log "pong"
Outputping
pong
response
Normally we would see the code above executed in the order in which it was written but because the $http.get
request above will take some time to get data
from our_url
, JavaScript won’t wait for that, instead, it goes ahead and executes the next line of code while waiting for $http.get
to finish what it is doing thus the arrangement of logging in the console. Other ways of writing code asynchronously include:
setTimeout
functions which schedule something to happen in the future and then go ahead to execute the next block of code:
console.log("Can I take your order?");
setTimeout(function() {
console.log("Yes please!");
}, 2000);
console.log("Sure, one second please");
OutputCan I take your order?
Sure, one second please
Yes please!
Higher order functions (also known as callback functions) which are functions that are passed to other functions as parameters. Let’s assume a callback function named function X
, it is then passed to another function called function Y
as a parameter. Eventually function X
is executed or called inside function Y
. Check out the code block below:
function Y(X) {
$.get("our_url", X);
}
Callback functions exist in different modes, chances are you’ve used them without knowing. Examples include window.onclick
, setTimeout
and setInterval
.
Vue watchers allow us to perform asynchronous operations in response to changing data. They’re like a more general way to react to data changes in your Vue instance. In our Vue Instance, watchers are denoted with the watch
keyword and are used thus:
new Vue({
el: '#app',
data: {
// data we intend to bind to our view
},
watch: {
// asynchronous operations that we'll be working with in our app
}
});
Let’s see how Vue uses watchers to monitor asynchronous operations. Using Vue, we’ll build an app with an infinite scroll feature, once a user gets to the bottom of the page, a GET
request is performed and more data is retrieved. Let me just chip in here that this article works through Sarah Drasner’s original pen. I’m a big fan. Let’s get to work:
Import the necessary tools via the DOM, using script tags. Here we’ll import Vue and Axios, a promise-based HTTP client for the browser
<head>
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
Create a new Vue Instance:
new Vue({
// The el property points to the DOM using a query selector
el: '#app',
});
Create the data
function and append any data that should be bound to the DOM. Initially, we won’t be at the bottom of the page thus the bottom
boolean property is set to false. Whatever results our API fetches will be stored in the beers
property, which is an empty array.
data() {
return {
bottom: false,
beers: []
}
}
Define our methods using the methods
member. Methods allow us to create functions and bind events to these functions as well as handle these events. In the bottomVisible()
function, we use three read-only properties to manually create our infinite scroll feature:
scrollY
: Returns the Y coordinate of the top edge of the current viewport. If there is no viewport, the returned value is zero.clientHeight
: The inner height of an element in pixels, including padding but not the horizontal scrollbar height, border, or margin.scrollHeight
: The height of an element’s content, including content not visible on the screen due to overflow.In the addBeer()
function, we perform a GET request using Axios. Using promises and callbacks, we create an object apiInfo
and pass in the values retrieved from our API call. Every function in our Vue Instance can access data properties using this
.
methods: {
bottomVisible() {
const scrollY = window.scrollY
const visible = document.documentElement.clientHeight
const pageHeight = document.documentElement.scrollHeight
const bottomOfPage = visible + scrollY >= pageHeight
return bottomOfPage || pageHeight < visible
},
addBeer() {
axios.get('https://api.punkapi.com/v2/beers/random')
.then(response => {
let api = response.data[0];
let apiInfo = {
name: api.name,
desc: api.description,
img: api.image_url,
tips: api.brewers_tips,
tagline: api.tagline,
food: api.food_pairing
};
this.beers.push(apiInfo)
if (this.bottomVisible()) {
this.addBeer()
}
})
}
}
The watch
option watches for changes to the state of the app and updates the DOM accordingly:
watch: {
bottom(bottom) {
if (bottom) {
this.addBeer()
}
}
}
We use the created
lifecycle hook to add a scroll event that fires each time the bottomVisible()
function is called. To implement our infinite scroll feature, we equate the bottom
boolean property in the data
function to the bottomVisible()
function. created
hooks allow us to access reactive data as well as functions in our Vue instance.
created() {
window.addEventListener('scroll', () => {
this.bottom = this.bottomVisible()
})
this.addBeer()
}
Move over to the DOM, where the following Vue directives will be used to provide two way binding between the DOM and the Vue instance:
v-if
: For conditionally rendering statements on the DOMv-for
: For rendering a list of items based on an array, requires a special syntax in the form of beer in beers
, where beers
is the source data array and beer
is an alias for the array element being iterated on.<div id="app">
<section>
<h1>Make yourself some Punk Beers </h1>
<div v-if="beers.length === 0" class="loading">Loading...</div>
<div v-for="beer in beers" class="beer-contain">
<div class="beer-img"> <img :src="beer.img" height="350" /> </div>
<div class="beer-info">
<h2>{{ beer.name }}</h2>
<p class="bright">{{ beer.tagline }}</p>
<p><span class="bright">Description:</span> {{ beer.desc }}</p>
<p><span class="bright">Tips:</span> {{ beer.tips }}</p>
<h3 class="bright">Food Pairings</h3>
<ul>
<li v-for="item in beer.food"> {{ item }} </li>
</ul>
</div>
</div>
</section>
</div>
One might ask “Why don’t we just use computed properties for this?”, the reason is that computed properties are synchronous and must return a value. When carrying out asynchronous operations such as timeout functions, or like our demo above, GET requests, it’s best to use watchers because Vue listens for the return value of the function. It’s also cool to use event listeners but these have the disadvantage of manually handling the event and calling a method instead of just listening to data changes. In any case, you now know how to handle asynchronous operations when you see or want to use them in Vue.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Sign up for Infrastructure as a Newsletter.
Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.
Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.