## infinite scroll
- 무한스크롤 구현 - 컨텐츠의 끝이 화면 끝에 닿으면 api가 호출되게 구현
- todolist 와 달리 외부 api 연동
- fetch, async, await
## API 연동
https://jsonplaceholder.typicode.com/ - 프로토타이핑용 api
- /posts 활용
- [json-server](https://www.npmjs.com/package/json-server) 만든 곳에서 만들었기 때문에 사용 방법은 json-server와 동일
1. 기본 html 구성
API 연동
https://jsonplaceholder.typicode.com/
- 이 사이트의 api는 test나 prototyping을 위한 API이다.
- 사이트에 들어가서 Resources의 posts를 활용했다.
- json-server 만든 곳에서 만들었기 때문에 사용 방법은 json-server와 동일 ( todolist에서 사용했음)
이번 무한스크롤 구현시, 페이지 네이션을 활용할 것.
1. 데이터 가져오기
;(function () {
'use strict'
const get = (target) => {
return document.querySelector(target)
}
const getPost = async () => {
const API_URL = 'https://jsonplaceholder.typicode.com/posts'
const response = await fetch(API_URL)
if (!response.ok) {
//ok가 아닌경우
throw new Error('에러발생')
}
//ok인 경우
return await response.json()
}
const loadPost = async () => {
const response = await getPost()
console.log(response)
}
window.addEventListener('DOMContentLoaded', () => {
loadPost()
})
})()
<코드설명>
code 1
window.addEventListener('DOMContentLoaded', () => {
loadPost()
})
기본적으로 DOMContentLoaded 를 이용하여, DOM이 다 불러와 졌을때, loadPost()가 동작하도록 작성한다.
code2
API URL 가져오기
https://jsonplaceholder.typicode.com/
JSONPlaceholder - Free Fake REST API
{JSON} Placeholder Free fake API for testing and prototyping. Powered by JSON Server + LowDB. Tested with XV. Serving ~2 billion requests each month.
jsonplaceholder.typicode.com
- 위 사이트이 들어간다.
- 스크롤을 내려 posts 클릭
- 이 url을 복사해서 사용하면 된다.
- 총 100개의 데이터가 있다.
const getPost = async () => {
const API_URL = 'https://jsonplaceholder.typicode.com/posts'
const response = await fetch(API_URL)
if (!response.ok) {
//ok이가 아닌경우
throw new Error('에러발생')
}
//ok이인 경우
return await response.json()
}
- async , await을 이용해 API_URL을 가져온다.
- await으로 받으면 const response = await fetch(API_URL) 와 같이 변수에 담을 수 있다.
- response가 ok가 아니면, throw new Error('에러발생') 로 에러를 발생시킨다.
- response가 ok인 경우, return await response.json() → resopnse를 return 해준다.
code3 → getPost에서 return한 response 받아오기
const loadPost = async () => {
const response = await getPost()
console.log(response)
}
console로 response를 출력해보면 다음과 같이 100개의 데이터가 잘 출력되는 것이 확인된다.
code4 → code3에서 받아온 response(100개데이터)를 하나하나 보여주는 작업하기
<div class posts></div> → 이곳에 post들을 넣어줄 것이다.
const loadPost = async () => {
const response = await getPost()
showPosts(response)
}
- getPost() 에서 받아온 데이터를 response 변수에 담아준다.
- showPosts()함수를 만들어 넘겨준다.
- showPosts()에서 데이터들을 하나하나 처리해 dom을 구성할 것이다.
showPosts() 함수 작성하기
const get = (target) => {
return document.querySelector(target) //dom element를 가져오는 get함수
}
const $posts = get('.posts')
const showPosts = (posts) => {
posts.forEach((post) => {
const $post = document.createElement('div')
$post.classList.add('post')
$post.innerHTML = `
<div class="header>
<div class="id">${post.id}</div>
<div class="title">${post.title}</div>
</div>
<div class="body">
${post.body}
</div>
`
$posts.appendChild($post)
})
}
1. posts데이터를 하나하나 처리하기 위해 forEach()를 사용한다.
// <div class posts></div> → 이곳에 post들을 넣어줄 것이다.
- post class를 가진 div를 만들어 준다.
- $posts에 appendChild로 $post를 붙여준다 .
- 확인해보면 100개의 div class=”post” 가 잘 들어간 것이 확인된다.
2. 페이지 끝 체크해서 데이터 추가로 가져오기
현재는 100개 데이터를 한번에 가져오기 때문에 제한을 걸어줄 것이다.
const limit = 10
const getPost = async () => {
const API_URL = `https://jsonplaceholder.typicode.com/posts?_limit=${limit}`
const response = await fetch(API_URL)
if (!response.ok) {
//ok이가 아닌경우
throw new Error('에러발생')
}
//ok이인 경우
return await response.json()
}
- limit 변수를 만들고 10을 할당한다 → 한번에 가져올 데이터 숫자
- getPost()의 API_URL로 다음과같이 limit을 적용해준다.
const API_URL = `https://jsonplaceholder.typicode.com/posts?_limit=${limit}`
10개만 화면에 보이는것이 확인된다 .
스크롤 끝 감지하기
window.addEventListener('DOMContentLoaded', () => {
loadPost()
window.addEventListener('scroll', onScroll)
})
scroll이벤트 발생시 onScroll 함수가 작동하도록 한다.
onScroll함수 작성하기
let page = 1
const getPost = async () => {
const API_URL = `https://jsonplaceholder.typicode.com/posts?_page=${page}&_limit=${limit}`
const response = await fetch(API_URL)
if (!response.ok) {
//ok이가 아닌경우
throw new Error('에러발생')
}
//ok이인 경우
return await response.json()
}
const onScroll = () => {
const { scrollTop, scrollHeight, clientHeight } = document.documentElement
if (scrollTop + clientHeight >= scrollHeight - 5) {
loadPost()
}
}
1. onScroll함수는 스크롤발생시 다음 값들을 받아온다.
const { scrollTop, scrollHeight, clientHeight } = document.documentElement
2. scrollTop + clientHeight >= scrollHeight - 5 일떄, loadPost()를 발생시킨다.
if (scrollTop + clientHeight >= scrollHeight - 5) {
loadPost()
}
3. page 변수를 만들어준다.
let page = 1
4. getPost 함수의 API_URL을 수정해준다.
const API_URL = `https://jsonplaceholder.typicode.com/posts?_page=${page}&_limit=${limit}`
네트워크텝으로 확인해보면 다음과같다.
++ page 를 2로 바꾸면 다음과같이 출력된다.
이제 할것은, 스크롤 끝이 닿을때마다, page를 늘려주는 것이다.
const onScroll = () => {
const { scrollTop, scrollHeight, clientHeight } = document.documentElement
if (scrollTop + clientHeight >= scrollHeight - 5) {
page++
loadPost()
}
}
위와같이 scrollTop + clientHeight >= scrollHeight - 5 일때마다 page++을 해주면 된다.
잘 동작하는 것처럼 보이지만, 문제점이 하나 생겼다.
문제점은, 페이지끝에 닿을떄마다, 이미 데이터를 다 가져왔는데도, 계속 불러온다는 것이다.
해결법 ==> 제한을 걸어준다.
const end = 100 //데이터의 총 갯수
let total = 10 // 여태까지 불러온 데이터의 개수를 나타낸다.
end , total 변수를 만들어준다.
const onScroll = () => {
const { scrollTop, scrollHeight, clientHeight } = document.documentElement
if (total === end) {
return
}
if (scrollTop + clientHeight >= scrollHeight - 5) {
page++
total += 10
loadPost()
}
}
2. onscroll 이벤트를 위와같이 수정해준다 .
- scrollTop + clientHeight >= scrollHeight - 5 일때마다, page++ , total을 10씩 증가시켜준다.
- total === end 일때, 종료시킨다.
- 확인해보면 이제 더이상 스크롤을 해도 불러오지 않는것이 확인된다.
마지막작업 → 다 불러온 후에는 scroll 이벤트를 제거해준다.
- 끝까지 닿으면, scroll 이벤트는 더이상 사용되지 않을 것이기 떄문이다.
const onScroll = () => {
const { scrollTop, scrollHeight, clientHeight } = document.documentElement
if (total === end) {
window.removeEventListener('scroll', onScroll)
return
}
if (scrollTop + clientHeight >= scrollHeight - 5) {
page++
total += 10
loadPost()
}
}
추가작업 → loading 애니메이션 노출시키기
<body>
<div class="wrap">
<h2>Infinite Scroll</h2>
<div class="posts"></div>
<div class="loader">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>
<script src="script.js"></script>
</body>
- index.html을 보면 loader라는 div가있다.
- opacity가 0으로 되어있다.
이것을 loading을할떄마다 opactiy를 1로만들어 노출시켜줄것이다.
const $loader = get('.loader')
const loadPost = async () => {
// 로딩 엘리먼트를 보여줌
showLoader()
try {
const response = await getPost()
showPosts(response)
} catch (error) {
console.error(error)
} finally {
//로딩 엘리먼트를 사라지게 함
hideLoader()
}
}
- loadPost를 try ~ catch ~ finally 구문으로 감싸준다.
- const $loader = get('.loader') → loader를 가져와준다.
.loader.show {
opacity: 1;
}
css 파일에 loader에 show 클래스가 붙으면 opacity가 1이되도록 작성해놓는다.
const hideLoader = () => {
$loader.classList.remove('show')
}
const showLoader = () => {
$loader.classList.add('show')
}
확인해보면, 스크롤되서 화면이 로드시 애니메이션이 잘 적용되는것을 확인할 수 있다.
'JavaScript' 카테고리의 다른 글
[JS] Proxy객체 맛보기 - 1단계 (0) | 2023.06.21 |
---|---|
[나의 toy 프로젝트] - 직접 Carousel 만들기 (0) | 2023.06.12 |
[JavaScript] Number.isInteger() 메서드 알아보기 p.s. 정수판별 (0) | 2023.04.17 |
[JavaScript] split() 메서드 알아보기 (문자열을 배열로 만들기) (0) | 2023.04.12 |
[JavaScript] join 메서드 알아보기 (배열을 문자열로 만들기) (0) | 2023.04.12 |