Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ClaytonSeager/InifinityScroll/llms.txt

Use this file to discover all available pages before exploring further.

Infinity Scroll monitors the user’s scroll position on every scroll event and automatically fetches a new batch of photos from the Unsplash API before the user ever reaches the bottom of the page. There are no “Load More” buttons and no page numbers — the gallery grows continuously as long as the user keeps scrolling.

The scroll trigger

A single scroll listener on window is responsible for deciding when to load the next batch. The condition combines three measurements of the page’s current state:
window.addEventListener('scroll', () => {
    const { innerHeight, scrollY } = window;
    const { offsetHeight } = document.body;
    if (innerHeight + scrollY >= offsetHeight - 1000 && ready) {
        ready = false;
        console.log('Fetching more photos...');
        getPhotos();
    }
    // ...
});
ExpressionWhat it measures
innerHeightThe visible height of the browser viewport in pixels
scrollYHow far down the page the user has scrolled
innerHeight + scrollYThe pixel position of the bottom edge of the viewport
offsetHeightThe full rendered height of the <body> element
offsetHeight - 1000A point 1000px above the very bottom of the page
When innerHeight + scrollY reaches or passes offsetHeight - 1000, the bottom of the viewport is within 1000 pixels of the end of the page. The second condition, && ready, ensures a fetch is not triggered while one is already in progress.
The 1000px lookahead buffer means new images begin loading well before the user runs out of content to scroll through, making the experience feel truly seamless rather than showing a sudden blank gap.

The ready flag

The ready boolean is the gatekeeper that prevents multiple concurrent fetches from firing at the same time.
  • Starts as false — the app is not ready to scroll-trigger a fetch until the first batch of images has fully loaded.
  • Set to false immediately when the scroll condition is met, blocking any further triggers for the duration of the fetch.
  • Set back to true only once every image in the current batch has fired its load event (inside imageLoaded()).
This means no matter how fast the user scrolls or how many scroll events fire, only one fetch can be in flight at any given time.

Fetch cycle

The complete journey from scroll to rendered images follows these steps:
1

Scroll event fires

The window scroll listener executes on every scroll.
2

Condition check

innerHeight + scrollY >= offsetHeight - 1000 && ready is evaluated.
3

ready = false

The flag is immediately set to false to block duplicate fetches.
4

getPhotos() called

An async fetch is made to the Unsplash API endpoint built by getApiUrl().
5

Unsplash API responds

The response JSON is parsed. For search results, data.results is used; for random photos, data is used directly.
6

displayPhotos() renders images

Each photo in the array is turned into an <a> + <img> element and appended to #Image__Container.
7

Each image fires its load event

As each <img> finishes downloading, its load event calls imageLoaded().
8

imageLoaded() increments the counter

imagesLoaded and totalLoadedImages are both incremented; the on-screen counter updates.
9

All images loaded → ready = true

When imagesLoaded === totalImages, ready is set back to true and the loading overlay is hidden.

Image loading tracking

Three variables work together to track batch progress:
VariableRole
totalImagesSet at the start of displayPhotos() to photosArray.length — the number of images in the current batch
imagesLoadedCounts how many images in the current batch have finished loading; reset to 0 after each complete batch
totalLoadedImagesA running total of every image loaded across all batches, displayed in the on-screen counter
The imageLoaded function handles all three:
const imageLoaded = async () => {
    console.log('Image loaded...');
    imagesLoaded++;
    totalLoadedImages++;
    imageCounter.textContent = totalLoadedImages;
    
    if (imagesLoaded === totalImages) {
        console.log('All images loaded...');
        imagesLoaded = 0;
        ready = true;
        loader.hidden = true;
    }
}
Each <img> element registers this callback through an inline event listener added in displayPhotos():
img.addEventListener('load', () => {
    imageLoaded();
});
Only after the very last image in a batch calls imageLoaded() does imagesLoaded === totalImages become true, flipping ready back to true and hiding the loader overlay.

Build docs developers (and LLMs) love