DEV Community

loading...
Cover image for A super simple implementation of Infinite Scrolling

A super simple implementation of Infinite Scrolling

sakun
building and breaking
・3 min read

I thought I’d share our approach to implementing infinite scrolling using jQuery on https://sideprojects.net and how it could be applied to other sites.

In our case, our pagination is in a “load more” format where you have to click the “load next days” button every time you reached the bottom of the page in order to show content from the previous 4 days.

What we wanted to do was display the next few days as soon as you scrolled to the bottom of the page and that’s what this tutorial will cover.

Setup

Prerequisites

  • A website with some sort of pagination.
  • Add jQuery to your website if you haven’t already.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js">
Enter fullscreen mode Exit fullscreen mode
  • Add a class to the button the controls your site’s pagination if you don’t have one already.

1) Create a window scroll event

//Infinite scroll
$(window).on("scroll", function() {

});
Enter fullscreen mode Exit fullscreen mode

2) Create a variable for the height of the entire document as well as a variable to determine your scroll position

Inside the scroll event, create 2 variables. We’re going to call them scrollHeight and scrollPos.

var scrollHeight = $(document).height();
var scrollPos = $(window).height() + $(window).scrollTop();
Enter fullscreen mode Exit fullscreen mode

3) Let's do some math!

Remember: we want the event to fire every time the user scrolls to the bottom of the page.

We can check if you’ve reached the bottom of a page with the following conditional statement:

if ((scrollHeight - scrollPos) / scrollHeight == 0) {
Enter fullscreen mode Exit fullscreen mode

We want the event to be triggered if the conditional statement is met.

In our case, the class name for our button is .load-more-days-button. That button will be clicked upon reaching the end of a page.

if ((scrollHeight - scrollPos) / scrollHeight == 0) {             
   $('.load-more-days-button').click();
}
Enter fullscreen mode Exit fullscreen mode

So putting it all together, we get:

//Infinite scroll
$(window).on("scroll", function() {
  var scrollHeight = $(document).height();
  var scrollPos = $(window).height() + $(window).scrollTop();
  if ((scrollHeight - scrollPos) / scrollHeight == 0) {
    $('.load-more-days-button').click();
    console.log("bottom!");
  }
});
Enter fullscreen mode Exit fullscreen mode

That’s all? Are we done?!? Well — yes, but no.

It works.

But it’s not great. Why?

  1. It requires you to scroll all the way down so that your scrollbar touches the end before it fires the event.
  2. It works poorly on mobile. I’ve noticed that it doesn’t always trigger on mobile and pick up the fact that you’ve reached the bottom of the page — even when you’ve scroll all the way down.

So let's fix it:

Let’s substitute this line:

if ((scrollHeight - scrollPos) / scrollHeight == 0) {
Enter fullscreen mode Exit fullscreen mode

with this instead:

if(((scrollHeight - 300) >= scrollPos) / scrollHeight == 0) {
Enter fullscreen mode Exit fullscreen mode

This line of code causes the scroll event to trigger when it is 300 (or less) pixels above the bottom of the page. (You can configure the 300 to something else if you’d like)

This works great on desktop and mobile!

Wrapping things up

Putting everything together, your code will probably end up looking something like this:

//Infinite Scroll
$(window).on("scroll", function() {
 //page height
 var scrollHeight = $(document).height();
 //scroll position
 var scrollPos = $(window).height() + $(window).scrollTop();
 // fire if the scroll position is 300 pixels above the bottom of the page
 if(((scrollHeight - 300) >= scrollPos) / scrollHeight == 0){
   $('.load-more-days-button').click();
  }
});
Enter fullscreen mode Exit fullscreen mode

You can view a live example here.

Discussion (8)

Collapse
trashpandadotnet profile image
trashpanda-dotnet

Hi,
thank you for this post. Most of the time I am a backend developer, but have to work on frontend code from time to time. Which is why I really like these little "How to's"
It really took me a while to understand this line of code

if(((scrollHeight - 300) >= scrollPos) / scrollHeight == 0)
Enter fullscreen mode Exit fullscreen mode

First thing that I do not quiet understand is that the boolean result of

(scrollHeight - 300) >= scrollPos // result is 0 or 1
Enter fullscreen mode Exit fullscreen mode

is devided by scrollHeight, I wouldn't consider this clean code. The division itself does not even add anything to the logic. The result of the division is checked to be zero. The result will only be zero if the check of

(scrollHeight - 300) >= scrollPos
Enter fullscreen mode Exit fullscreen mode

returned fales. Which means we could just write:

if(((scrollHeight - 300) >= scrollPos) == false)
Enter fullscreen mode Exit fullscreen mode

I would even go further and change the operators, reuslting in:

if((scrollHeight - 300) < scrollPos)
Enter fullscreen mode Exit fullscreen mode

Am I missing something?

Collapse
kedzior_io profile image
Artur Kędzior • Edited

Few improvements here:

  1. For the love of whatever you believe in use Typescript. Why would anyone not use Typescript? :-)
  2. This solution kind of works but it will get triggered several times and if you want to fetch stuff from API it will make many unnecessary requests. So how about this:
$(window).on("scroll", function () {
    const scrollHeight = $(document).height() as number;
    const scrollPos = Math.floor($(window).height() + $(window).scrollTop());
    if (scrollHeight === scrollPos) {
        console.log("Scroll MF!");
    }
});
Enter fullscreen mode Exit fullscreen mode

to make it even better, we can make it execute before hitting the bottom and again executing only once:

let page = 1;
let currentscrollHeight: 0;
let lockScroll: false;

$(window).on("scroll", () => {
    const scrollHeight = $(document).height() as number;
    const scrollPos = Math.floor($(window).height() + $(window).scrollTop());
    const isBottom = scrollHeight - 300 < scrollPos;

    if (isBottom && currentscrollHeight < scrollHeight) {

        $.ajax({
            method: "POST",
            url: "/api/search",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            data: JSON.stringify({ page: this.page })
        } as any)
        .done((posts: Post[]) => {
            this.page = this.page + 1;
            // do whatever
        });

        currentscrollHeight = scrollHeight;
    }
});
Enter fullscreen mode Exit fullscreen mode

Check an example: fullscreen-modal.azurewebsites.net

Collapse
jsdgraphics profile image
jsdgraphics

Dude! i made an account here just to thank you men, i spend a whole day trying to make this script work with a custom post type in wordpress, i end up in this article again after close it (i didn't read the comments the first time) and i realize that the main problem for me was "if(((scrollHeight - 300) >= scrollPos) / scrollHeight == 0)".

The script ran like four times every time, i just use your code but with my ajax and bum! thanks a lot good man

Thread Thread
kedzior_io profile image
Artur Kędzior

Awesome! I'm glad it helped you!

Collapse
thepeoplesbourgeois profile image
Josh

Nice work! Have you seen the IntersectionObserver API? It might allow you to simplify this code even further 😁

Collapse
abhishekcghosh profile image
Abhishek Ghosh

Would strongly second checking out the IntersectionObserver API. Scroll handlers can come with a lot of performance bottlenecks (even if debounced) in execution that blocks the main thread (js is single threaded in nature), slowing everything else on the page .. you might see stuff work smoothly on a developer desktop but when you test on a low end device like an average phone (what your users may be using), you'll see frame drops and jank :( IOs provide async ways to solve problems like this and has been a great addition to the web! If you absolutely need to use scroll listeners, try exploring if "passive" scroll listeners will work for you.

Collapse
sakun profile image
sakun Author

Ah that looks cool. Will be sure to check that out, thanks!

Collapse
anirudhbagri profile image
Anirudh Bagri

This is great!

I have a page that is empty (no header/footer or any-body) and is supposed to put everything that comes from an API in an infinity scrollable page.
However, When my page starts with an empty document, it doesn't load anything?
Of course, I can call the function once or twice to initialize with some data.. but is that correct?