데이터를 받아 오는 동안 스피너 혹은 스켈레톤ui를 띄어보자.
html
<ul>
<!-- 초기에 4개의 li를 생성하여 화면에 표시 -->
<li class="test">
<div class="left-content">
<img src="https://picsum.photos/id/1/536/354" alt="img">
</div>
<div class="right-content">
<h3>게시물 1번 째</h3>
<div>Lorem ipsum dolor sit amet.</div>
</div>
</li>
<li class="test">
<div class="left-content">
<img src="https://picsum.photos/id/2/536/354" alt="img">
</div>
<div class="right-content">
<h3>게시물 2번 째</h3>
<div>Lorem ipsum dolor sit amet.</div>
</div>
</li>
<li class="test">
<div class="left-content">
<img src="https://picsum.photos/id/3/536/354" alt="img">
</div>
<div class="right-content">
<h3>게시물 3번 째</h3>
<div>Lorem ipsum dolor sit amet.</div>
</div>
</li>
<li class="test">
<div class="left-content">
<img src="https://picsum.photos/id/4/536/354" alt="img">
</div>
<div class="right-content">
<h3>게시물 4번 째</h3>
<div>Lorem ipsum dolor sit amet.</div>
</div>
</li>
</ul>
css
ul {
padding-left: 0;
}
ul li {
background-color: #c0c0c0;
margin-bottom: 20px;
list-style: none;
text-align: center;
color: #fff;
padding: 30px 0;
display: flex;
}
/* 짝수, 홀수 색상 구분하기 위해 */
li:nth-child(2n) {
background-color: #555;
}
ul li {
display: flex;
}
ul li > div {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.left-content {
flex: 0.3;
}
.right-content {
flex: 0.7;
}
/* 스피너 스타일 */
.spinner-bg {
width: 100%;
padding: 20px 0;
background: #eee;
}
.spinner {
border: 10px solid rgba(255, 255, 255, 0.5);
border-top: 10px solid rgb(45, 184, 45);
border-radius: 50%;
width: 100px;
height: 100px;
animation: spin 2s linear infinite;
margin: 0 auto;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
javascript
function startInfinite() {
const $ul = document.querySelector('ul');
let count = 5; // 시작 값을 5로 변경
// 1. 인터섹션 옵저버 생성
const io = new IntersectionObserver((entry, observer) => {
const tests = document.querySelectorAll('.test');
console.log(tests.length);
// 3. 현재 보이는 target 출력
const ioTarget = entry[0].target;
if (entry[0].isIntersecting) {
console.log('현재 보이는 타겟', ioTarget);
// 5. 현재 보이는 target 감시 취소해줘
io.unobserve(ioTarget);
// 6. 스피너 표시
const spinner = document.createElement('div');
spinner.className = 'spinner';
const loading = document.createElement('div');
loading.className = 'spinner-bg';
loading.appendChild(spinner);
$ul.appendChild(loading);
// 데이터 가져오기 (지연 처리 콜백 함수 사용)
setTimeout(function () {
const dataUrl = 'https://jsonplaceholder.typicode.com/posts';
fetch(dataUrl)
.then(response => {
if (!response.ok) {
throw new Error('데이터를 불러오는 데 실패했습니다.');
}
return response.json();
})
.then(data => {
// 데이터 가져오면 스피너 제거하고 내용 추가
$ul.removeChild(loading);
for (let i = 0; i < 4; i++) {
// 6. 새로운 li 추가해
const $li = document.createElement('li');
$li.className = 'test';
// 현재 카운트 값을 사용하여 내용을 추가
$li.innerHTML =
`
<div class="left-content">
<img src="https://picsum.photos/id/${count}/536/354" alt="img">
</div>
<div class="right-content">
<h3>게시물 ${count}번 째</h3>
<div>${data[count - 1].title}</div>
</div>
`;
$ul.appendChild($li);
count++; // 카운트 증가
}
// 7. 새로 추가된 li 감시해!
io.observe($ul.lastChild);
});
}, 1500);
}
}, {
// 8. 타겟이 50% 이상 보이면 해줘!
threshold: 0.5
});
// 2. 초기 4개의 li 요소를 감시해!
const initialLiElements = document.querySelectorAll('li');
initialLiElements.forEach(li => {
io.observe(li);
});
}
startInfinite();
css
.skeleton {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0f0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: loading 1.5s infinite;
padding: 50px;
}
@keyframes loading {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
.hidden {
display: none;
}
javascript
// 6. 스켈레톤 UI 표시
const skeletonLi = document.createElement('li');
for (let i = 0; i < 4; i++) {
const skeletonLi = document.createElement('li');
skeletonLi.className = 'skeleton';
$ul.appendChild(skeletonLi);
}