Infinite Gallery Viewer with Typescript

Infinite Gallery Viewer with Typescript

In our previous series release Build a quote generator with typescript, we converted a regular Javascript code to Typescript, and it was fun. In today's release, we'd be converting an Infinite Gallery viewer built with Javascript to Typescript.

Disclaimer: This article is part of ZTM Andre Negeoi's Portfolio enhancement course

The Javascript codebase is on GitHub. You should obtain the site structure and stylesheet from there. We'd focus on the Typescript conversion here.

Setup Typescript

If you need help setting up your Typescript environment for this exercise, see the setup from our last series here

Getting an Unsplash API key

Head on to Unsplash and register, next click on api/developers tab from the profile menu, then click on Your apps. Follow the guide to create a new app and get your API_KEY.

Writing the App.ts

Let's start with our variables and interface definitions:

const imageContainer = document.getElementById('image-container');
const loader = document.getElementById('loader');

let photosArray: GalleryData[];
let ready = false;
let imagesLoaded = 0;
let totalImages = 0;
let count = 5;
const API_KEY = <your_api_key>;
let API_URL = `https://api.unsplash.com/photos/random/?client_id=${API_KEY}&count=${count}
`;

interface GalleryData {
  alt_description: string;
  urls: {
    regular: string;
  };
  links: {
    html: string;
  }
}

interface Attribute {
  src?: string;
  alt?: string;
  href?: string;
  title?: string;
  target?: string;
}

The functions

Our first function would be the imageLoaded function.

// Check if all images were loaded
function imageLoaded() {
  imagesLoaded++;

  if (imagesLoaded === totalImages) {
    ready = true;
    loader!.hidden = true;
    count = 30;
    API_URL = `https://api.unsplash.com/photos/random/?client_id=${API_KEY}&count=${count}
`;
  }
}

The special conversion highlight here is loader!.hidden = true; When we defined the loader element initially with this const loader = document.getElementById('loader');, we could either have the HTMLElement or null. To tell typescript that we are sure that the element exists on the page, we use the ! before the element name. That overrides typescript's strict null check.

Pro-Tip: Use only when you are double sure that the element exists on the page and will not be removed throughout the lifetime of your code.

setAttribute Helper function

This function will help us stay DRY. Typescript warns when we do not type our parameters. Our function would need

  • A HTMLElement to set the attributes against, this will be the first parameter.
  • An object that contains the attributes we want to set and their values.
// Helper function to set attributes on DOM elements
function setAttributes(element: HTMLElement, attributes: Attribute) {
  for (const [key, value] of Object.entries(attributes as Record<string, string>)) {
    element.setAttribute(key, value);
  }
}

Pro Tip: To use Object.entries, ensure you add "ES2017" or later to your tsconfig.json's lib.

displayPhotos function

The displayPhotos function generates the DOM elements and renders it to the screen.

// Display photos
const displayPhotos = () => {
  totalImages = photosArray.length;
  imagesLoaded = 0;

  photosArray.forEach(photo => {
    // Create <a> linking to unsplash
    const item = document.createElement('a');
    setAttributes(item, {
      href: photo.links.html,
      target: '_blank'
    })

    // Create <img> for photo
    const img = document.createElement('img');
    setAttributes(img, {
      src: photo.urls.regular,
      alt: photo.alt_description,
      title: photo.alt_description
    });

    // Event Listerner, check when each is finished loading
    img.addEventListener('load', imageLoaded)

    // Put <img> inside <a>, then put both in the imageContainer;
    item.appendChild(img)
    imageContainer!.appendChild(item);
  });
}

Finally, we will add the getPhotos function and attach our scroll event to the windows object.

// Get photos from Unsplash API
const getPhotos = async () => {
  try {
    const response = await fetch(API_URL);
    photosArray = await response.json()

    displayPhotos();
  } catch (error) {

  }
}

// Hook up the scroll event
window.addEventListener('scroll', () => {
  if (
    window.innerHeight + window.scrollY >=
    document.body.offsetHeight - 1000 &&
    ready
  ) {
    ready = false;
    getPhotos();
  }
})

// On load
getPhotos();

Now run the tsc --build tsconfig.json to build your .ts files into the required .js equivalence.

This was super exciting for me, I hope you enjoy it.