Infinite Scroll - 1

#Base Function

  • getList

    • 5개의 게시글을 a비동기적으로 불러오게 되는 함수
  • renderItem

    • 하나의 게시글을 DOM 요소로 변경하는 함수
  • fetchMore

    • $footer 에 loading 을 해준후

    • 비동기로 list를 불러옴

    • documentFragment 를 이용하여 $app 요소까지 부착하게 되는 작업

    • loading 표시를 제거해줌

#Main Function

  • 기본적으로 window에 scroll시에 onScroll 을 작동하게 합니다.
  • clientHeight 는 element의 내부 높이입니다.
    • 내부 여백(padding) 을 포함하고, 수평 스크롤바의 높이, 경계선 외부 여백(margin)을 포합하지 않습니다.
  • scrollTop 은 element 최상단과 보여지는 컨텐츠와의 거리를 의미합니다.
    • 세로 스크롤가 없다면 scrollTop 은 항상 0이 됩니다
  • ScrollHeight 는 보이지 않는 부분까지 전체의 높이를 나타냅니다
  • 결론적으로 스크롤이 제일 바닥으로 내려온 상태가 되면 clientHeight + scrollTop = ScrollHeight 임으로 게시글을 5개 더 불러오게 됩니다.

#Issue

  • scroll 이 발생할때 마다 onScroll이 동작하고 계산을 하게 됩니다.

  • 최적화된 Web API 인 Intersection Observer 를 사용하여 개선할 수 있습니다.

    • 타겟 요소와 상위요소 또는 최상의 document의 사이의 intersection내의 변화를 관찰합니다

    • Infinite Scroll - 2

<div id="app"></div>
<div class="footer"></div>
const $app = document.querySelector('#app')
const $footer = document.querySelector('.footer')

const getList = (count) => {
  return new Promise(resolve => {
    setTimeout(() => {
      const data = Array.from({length:5}).map((_,idx)=>{
        const id = (count*5)+idx+1
        return {id, data: `${id}번째 게시글입니다`}
      })
      resolve(data)
    },1000)
  })
}

const renderItem = ({id, data}) => {
  const item = document.createElement('li')
  item.innerHTML = `
    <div class="item-id">${id}</div>
    <div class="item-data">${data}</div>
  `
  return item
}
let count = 0
const fetchMore = async () => {
  $footer.classList.add("loading");
  const list = await getList(count ++)
  const frag = document.createDocumentFragment()
  list.forEach(item=> {
    frag.appendChild(renderItem(item))
  })
  $app.appendChild(frag)
  $footer.classList.remove("loading");
}

const onScroll = (e) => {
  const {clientHeight,scrollTop,scrollHeight} = e.target.scrollingElement;
  if (clientHeight + scrollTop === scrollHeight) {
    fetchMore()
  }
}

fetchMore()
window.addEventListener('scroll',onScroll)
.footer {
  height : 50px;
  line-height: 50px;
  text-align:center;
}

#app li {
  display:flex;
  margin: 10px;
  border:1px solid black;
  height : 20vh;
  text-align :center;
  line-height: 20vh
}
.item-id {
  border-right: 1px solid black;
  width : 20%;
}
.item-data {
  width : 80%;
}

.loading:after {
  content: "... 로딩중 ...";
}