Lazy Load Images with Vanilla Javascript
Tags: javascript, html, css
Published: 2019-05-06 21:00:29

## What is Lazy Loading?

Surely many of you have already heard that Google Chrome is going to add Lazy Loading in a native way with HTML tags, for images and iframes.

What Lazy Loading basically means is not to load the content of these images until the user does not have them in the field of view. In this way, an image located at the end of the web page will never be loaded if the user does not reach that point. With this solution, we save loading time and gain speed.

In this tutorial we'll lazy load 30 images, previously fetched from an API. First, we'll write the HTML, then the JS code and finally we'll implement some basic CSS to have a beautiful result. Here you have a demo:

{% codepen https://codepen.io/GuimG/pen/wbByyY %}

## Our HTML skeleton

This is our `index.html` file. It just has a title, a loading spinner and an empty `div` where we'll put all the images from the API.

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Lazy Loading Images</title>

    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <h1>Lazy Loading Images</h1>

    <!-- Images Container -->
    <div id="imagesContainer"></div>

    <!-- Loading spinner -->
    <div class="lds-roller" id="lds-roller">
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
    </div>

    <!-- Our custom JS code -->
    <script src="main.js"></script>
  </body>
</html>
```

At this point, our page is empty. Let's create our `main.js` file, grab some images and lazy load them.

## Here is where the magic happens

As this file will be more complex, let's split the code into smaller parts so we can deeply understand each of them. All of the following code it's written in the `main.js` file.

```javascript
window.onload = () => {
  // Get 30 random images
  fetch('https://picsum.photos/v2/list?limit=30')
    .then(function(response) {
      return response.json();
    })
    .then(function(myJson) {
      // Call the createImages function to generate the HTML code
      createImages(myJson);
      // Remove the loading spinner
      document.getElementById('lds-roller').remove();
    })
    .catch(err => {
      console.log(err);
    });
};
```

This first function will be triggered ones the window is ready. We use the `fetch` API to get the images from an external API. This API response us with a JSON file containing all the url's to the images. With this url's we can now create our HTML code.

```javascript
function createImages(imgs) {
  for (let i of imgs) {
    // Create an image HTML tag
    const image = document.createElement('img');
    image.setAttribute('data-lazy', i.download_url);
    image.classList.add('lazy-loading');
    document.getElementById('imagesContainer').appendChild(image);
  }
  // Sets an observer for each image
  lazyTargets = document.querySelectorAll('.lazy-loading');
  lazyTargets.forEach(lazyLoad);
}
```

Note that we're not setting the `src` attribute of the images. We're creating a `data-lazy` one. This way, the images don't request any data when they are created. Now let's do the lazy-loading function. This function will use the `intersection observer` API to know when each image is on the view. Then it'll replace the `src` attribute with the `data-lazy` one.

```javascript
function lazyLoad(target) {
  const obs = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const img = entry.target;
        const src = img.getAttribute('data-lazy');

        img.setAttribute('src', src);
        img.classList.add('fadeIn');

        observer.disconnect();
      }
    });
  });
  obs.observe(target);
}
```

## Let's make it pretty

Now I've put some basic CSS to make things prettier. The file referenced in the HTML is `style.css`.

```css
html,
body {
  margin: 0;
  padding: 0;
  width: 100%;
}

h1 {
  text-align: center;
  font-family: sans-serif;
  font-weight: 200;
}

#imagesContainer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  margin: auto;
  width: 680px;
}

#imagesContainer img {
  width: 300px;
  min-height: 200px;
  margin: 20px;
  box-shadow: 5px 10px 15px;
}

@keyframes fadeIn {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

.fadeIn {
  animation-name: fadeIn;
  animation-duration: 3s;
}
```

So that's all! Here's the link of [CodePen](https://codepen.io/GuimG/full/wbByyY). See you soon!