infinite Scroll 구현하기

들블리셔·2023년 10월 18일
0

무한 스크롤

javascript IntersectionObserver을 통해서 Infinite Scroll을 구현 해 보고,

데이터를 받아 오는 동안 스피너 혹은 스켈레톤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();



스켈레톤 ui 적용시 일부 코드만 변경

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);
}
profile
나의 공부방

0개의 댓글