Build a responsive image gallery with CSS grid, JavaScript and the unsplash API
Tags: html, css, javascript, codenewbie
Published: 2019-11-07 18:26:20

Originally posted on [my blog](https://www.ibrahima-ndaw.com/blog/build-a-responsive-image-gallery/) <hr>

CSS grid is awesome, we can't say it enough. It helps a lot in making our website responsive. Sometimes we even don't need media queries to make our layout fully responsive. In this post, I will lead you on how to build a responsive image gallery with CSS grid (only) and some JavaSript (to make it reactive), and the images will be fetched from the Unsplash API. So you'll need to create an account [here](https://unsplash.com/developers) to get your API key. 

- [HTML](#html)
- [CSS](#css)
- [JavaScript](#javascript)

## HTML
The HTML is relatively simple as you can see.

```html
    <main>
      <div class="image--container"></div>
      <div class="image--selection"></div>
    </main>
```
We have two div elements, the first with `image--container` class will be the viewer and the second `div` tag will enable us to select an image from the gallery. And by the way img tags will be created by JavaScript.

## CSS

For the CSS, we will first make some resets to remove spaces and import `Open Sans` from google fonts.

```css
@import url('https://fonts.googleapis.com/css?family=Open+Sans:700&display=swap');
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  background: #444;
}

main {
  padding: 0 0.5rem;
  height: 100vh;
}
```
Then, change the `background-color` for the `body` tag and set the height of the `main` tag to fit the viewport height.

```css
.image--gallery {
  width: 100%;
  height: 32rem;
  display: block;
  margin: auto;
  border: 5px solid #222;
  object-fit: cover;
}

.image--selection {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  justify-content: center;
  align-items: center;
}

.image__item {
  width: 100%;
  height: 8rem;
  display: block;
  margin: auto;
  margin-bottom: 0.5rem;
  border: 5px solid #222;
  object-fit: cover;
  cursor: pointer;
}
```
To make the image viewer looking nice, we have to use `.image--gallery` to expand the width to 100% to have a responsive image and the most important set the `object-fit` property to `cover`. It will just ensure that the image fit its container element. <br>
Then, for the image gallery, we use `.image--selection` to have a grid system. After displaying it in grid, we have to set `grid-template-columns` to `repeat(auto-fit, minmax(9rem, 1fr))`.<br> 
The `repeat()` function takes two arguments: the number of times to repeat the value and the value itself. The `auto-fit` value allows us to wrap our columns into rows when we lack spaces to display every elements in the same column in our viewport. And the `minmax()` function will set the minimum size of the element to `9rem` and the maximum size to `1fr`.<br>
`justify-content` and `align-items` help us centering the grid elements.<br>
Finally, the `.image__item` class serves to resize each image in the gallery.<br>
`rem` is a relative unit of the font size(16px).<br>
`fr` is just a fractional unit.

```css
.loader {
  text-align: center;
  margin-top: 20%;
  color: #fff;
  font-family: 'Open Sans', sans-serif;
  font-weight: 700;
}

.animate-entrance {
  animation: BounceIn 0.8s;
}

@keyframes BounceIn {
  0% {
    transform: scale(0.8);
    opacity: 0;
  }
  100% {
    transform: scale(1);
    opacity: 1;
  }
}
```
As you can see here, we use `.loader` class to style the alignment, font, margin and color of the text to display if we have no images.<br>
Then, `.animate-entrance` class helps us making some animations on the image viewer. For that, we use the animation property and the `BounceIn` value defined below with `@keyframes`. It will just bounce in with a fade in effect and scale a little bit. And the animation will last 0.8 seconds.

## JavaScript
We start the javaScript part by selecting some elements.

```javascript
const image_gallery = document.querySelector('.image--container');
const image_container = document.querySelector('.image--selection');
const loading = '<h1 class="loader">Loading...</h1>';

```
The first `image_gallery` is the viewer element, and the second `image_container` is the image gallery. Both elements are selected by their class names with the `querySelector` method.
The last one `loading` will be used if we have no images to show.

```javascript
const showImages = () => {
  // Show loading text if no data
  if (image_container.children.length === 0)
    image_container.innerHTML = loading;
  fetch(
    `https://api.unsplash.com/photos?client_id=${apiKey}`
  )
    .then(res => {
      res.json().then(images => {
    // Call the function to create the gallery
        createImageGallery(images);
      });
    })
    .catch(e => {
      console.log(e);
    });
};
```
The `showImages()` function will display images fetched from the unsplash API. First, we need to check if the `image_container` have a child element or not, in other words if the image gallery is created in the DOM or not and display `Loading...` while fetching data from unsplash with the fetch API. Then, if we get images pass it to the `createImageGallery()` function as an argument to display images otherwise catch the error. <br>
For the `apiKey` in the url, you need to create an account [here](https://unsplash.com/developers) and replace it with your API key.

```javascript
const createImageGallery = images => {
  let output = '';
  // Show the first image on the viewer
  image_gallery.innerHTML = `<img src="${images[0].urls.regular}" class="animate-entrance image--gallery" alt="${images[0].alt_description}">`;
  setTimeout(() => {
    image_gallery.children[0].classList.remove('animate-entrance');
  }, 500);
  // show unselected images
  images.forEach(({ urls, alt_description }) => {
    output += `<img src="${urls.regular}" alt="${alt_description}" class="image__item" />`;
  });
  image_container.innerHTML = output;
};
```
To create the viewer in the DOM, we use the property innerHTML to append the element to `image_gallery`. Then, we remove `animate-entrance` class after 0.5 seconds to be able to animate the viewer again.<br>
And we loop through `images`(data fetched from unsplash) and just pull out `urls` and `alt_description` to create the img tag and append it to `image_container` to create the image gallery's selection on the DOM.

```javascript
const changeImage = e => {
  // Get the viewer image element
  const image = image_gallery.children[0];
  if (e.target.src) {
    // change the image
    image.classList.add('animate-entrance');
    image.src = e.target.src;
    setTimeout(() => {
      image.classList.remove('animate-entrance');
    }, 800);
  }
};

// Event listeners
document.addEventListener('DOMContentLoaded', showImages);
image_container.addEventListener('click', changeImage);
```
To change the image on the viewer, we need to get the child element(img tag) of `image_gallery`. Then, check if the event provided as an argument contains an src attribute of not. And then change the viewer image with the selected one and add the `.animate-entrance` class to the img tag. Then, we remove `animate-entrance` class after 0.5 seconds to be able to animate the viewer again.<br>
And to make it happen, we need to listen to two events. The first `DOMContentLoaded` will fire the `showImages()` function to display all images when the initial HTML document will be completely loaded.
The second listen for the click event on the `image_container` element and change the viewer image with the function `changeImage()`.   

You can check it live [here](https://codepen.io/ibrahima92/full/zYYqqbZ)<br>

![stepper-form](https://drive.google.com/uc?id=113hAhsvHsweU-LuqgZIv9L60_QQfE28l)

That's all folks!!!

| [TWITTER](https://twitter.com/ibrahima92_) | [BLOG](https://www.ibrahima-ndaw.com/) | [NEWSLETTER](https://ibrahima-ndaw.us5.list-manage.com/subscribe?u=8dedf5d07c7326802dd81a866&id=5d7bcd5b75) | [GITHUB](https://github.com/ibrahima92) | [LINKEDIN](https://www.linkedin.com/in/ibrahima-ndaw/) | [CODEPEN](https://codepen.io/ibrahima92) | [DEV](https://dev.to/ibrahima92) |
| -------------- | ------------------------------------------ | ----------------------------------------- | ----------------------------------------------------------------------------------------------------------- | ----------------------------- | ------------------------------------------------------ | ---------------------------------------- | -------------------------------- |