Lazy Loading with the IntersectionObserver API
Tags: javascript, lazyloading, polyfill
Published: 2019-01-11 08:40:48

Hi. In this post, I'll give some information about [IntersectionObserver API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).

Before we start, I should say this IE11 doesn't support this API. You can check which browsers support on the [CanIUse](https://caniuse.com/#search=IntersectionObserver).

## Why?

There are many libraries that helping lazy loading operations. I know, but this API native. You may don't want to use external libraries etc.

In a project, we had many images and many list items. Because of that images, the page was loading in 10-15 seconds. This was terrible for us and our customers, visitors etc.

We found some solutions like server-side pagination. But this wasn't a good solution for our project. Because customers didn't want to reload the page.

In order to solve the problem, we found Verlok's [lazyload](https://github.com/verlok/lazyload) library. But we also found **IntersectionObserver** API. Anyway, we decided to use IntersectionObserver API. We're using IntersectionObserver today. Okay, I'll show how.

## HTML && CSS Codes

In this example, we'll use the Bootstrap 4's Card component. We also use the button component for tracking the index. Default index will be 0(zero).

```html
<div class="container">
	<div class="row">
		<div class="col-md-12 card-container">
			
		</div>
	</div>
	<div class="row">
		<div class="col-md-12 load-more-container">
			<button data-index="0" class="btn btn-primary btn-block load-more">Load More</button>
		</div>
	</div>
</div>
```

And the CSS will be like this. Just a little bit margin. Our cards look more pretty now.

```css
.load-more-container {
	margin-top: 15px;
}

.card {
	margin-bottom: 15px;
}
```

## JavaScript Codes

I'll create a class. You may want to create a function. This depends on you. The class will start like this. Its name will be **LazyLoad**.

```js
class LazyLoad {
    constructor() {
	this.btn = document.querySelector(".load-more")
	this.cardContainer = document.querySelector('.card-container')
	this.BASE_URL = 'https://picsum.photos/list'
    }
}
```

We will API server for the images.

*In this example, we always call all the images from this server. Because I couldn't find any pagination.*

Then, I'll create a function called **load**.

```js
load() {
		fetch(this.BASE_URL)
		.then(resp => resp.json())
		.then(obj => {
			let start = (this.btn.dataset.index == 0) ? parseInt(this.btn.dataset.index) : parseInt(this.btn.dataset.index) + 1
			let end = start + 5
			let arr = obj.slice(start, end)
			if(arr.length > 0) {
				let cards = '';
				arr.forEach(f => {
					cards += `
						<div class="card">
						  <div class="card-body">
							<h5 class="card-title">${f.author}</h5>
							<img src="https://picsum.photos/458/354/?image=${f.id}" alt="">
						  </div>
						</div>
					`
				})
				
				this.cardContainer.insertAdjacentHTML('beforeend',cards)
				this.btn.dataset.index = end
			}
		})
	}
```

With this function, we will create fetch requests. We will also create HTML content thanks to template literals to put them to `.card-container`.

And the last thing, we will create a function called **init**. The function will be like this;

```js
	init() {
		const container = document.querySelector(".load-more-container")
		let observer = new IntersectionObserver(entries => {
			entries.forEach(entry => {
				const { isIntersecting } = entry

				if(isIntersecting) {
					this.load()
					//observer = observer.disconnect()
				}
			}, {
				root: container
			})
		})

		observer.observe(container)
	}
```

In the above code, when the element intersecting with the user' viewport codes will be run. IntersectionObserver gives two arguments. The first argument will be a callback. The second argument will be options. In this example, we have only `root` options.

The callback that receives one or more intersection entries. And observer will be observing the container we created.

```js
observer.observe(container)
```

Using `observer = observer.disconnect()` you will stop observing operations. Which means, more load functionality will not work.

## Before Finishing

If you want to a real example, I've created an example on the CodePen. You can check it how it works.

{% codepen https://codepen.io/aligoren/pen/wRYpQy %}

## Resources

[MDN](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/IntersectionObserver)

[David Walsh](https://davidwalsh.name/intersection-observers)

I hope this post will help you with your projects.

**Thanks for reading!**