상세 컨텐츠

본문 제목

시베리안 허숙희의 자바스크립트 비기닝 32 (무한 스크롤)

자바스크립트

by e7e 2023. 6. 28. 17:36

본문

2023.06.28 - [자바스크립트] - 시베리안 허숙희의 자바스크립트 비기닝 30

글에서 페이지 나누기를 해보았당.  모바일에서 많이 사용되지만 요즘은 데스크탑에도 그냥

거부감없이 사용되고 있는 무한스크롤을 구현해 보장!

 

사용자들이 항상 보여지는 모든 글을 읽는 건 아닐 거시당. 무겁게 리스트를 다 가져 왔는데

몇개 보다 다른 곳으로 갈 수도 있당.  아주 낭비당. 그래서 무한 스크롤은 일부만 보여주다가

사용자가 스크롤을 끝까지 내리기 직전에 다음 데이타를 가져오는 식으로 구현된당.

당연히 주로 AJAX와 함께 사용될 거시당.

 

아래 소스는 google 뉴스를 검색으로 가져오고, 일부만 보여준다메, 스크롤을 내리면

가져온 데이타의 나머지도 보여지는 방식으로, 무한스크롤이 주로 사용되는 배경과 

쪼메 다르긴 하지만, 무한 스크롤 구현 원리만을  보여주기 위함이니, 양해가 필요함.

스크롤 이벤트에 주목해서 보면 크게 어렵진 않을 것이다.

아래 그림을 머리에 담고서 scrollTop + clientHeight = scrollHeight이면  스크롤이

맨 끝까지 내려 온 거심을  확실히 이해하고서 소스를 참고하도록 하장 

무한스크롤기본.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>무한루핑</title>
<style>
    .content {
        border:10px solid gold;
    }
</style>
<!-- xml <-> json 라이브러리 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/x2js/1.2.0/xml2json.min.js"></script>
</head>
<body>
    <h1>무료우회서버 사용해서 마니 느려용</h1>
    <input type="text"  id="schWord" value="" onkeydown="fEnter()"><button onclick="fSch()">껌색</button>
    <div id="disp">
    </div>

<script>
    const mySchWord = document.querySelector("#schWord");
    function fEnter(){
        if(event.keyCode == 13){
            dataCnt = 1;
            fInit();
        }
    }

    function fSch(){
        if(mySchWord.value.trim().length < 2){
            alert("검색어가 너무 짧아용");
            return;
        }
        dataCnt = 1;
        fInit();
    }

    const myDisp  = document.querySelector("#disp");
    const x2js = new X2JS();
    let schDatas;
    let dataCnt = 1;
    let perCnt = 10;
    let msgOpen  = false;  // 데이타 없다는 메세지 한번만 뜨겡!
    
    function fList(){
        let itemStr ="";
        let starIndex = (dataCnt -1) * perCnt;
        if(starIndex >= schDatas.length){
            if(msgOpen) return;
            alert("더 이상 데이터가 없어용 쏘링");
            msgOpen = true;
            return;
        }
        let endIndex = starIndex + perCnt;
        if(endIndex > schDatas.length){
            endIndex = schDatas.length;
        }

        for(let i=starIndex; i < endIndex ; i++){
            itemStr += `
            <div class="content">
                <table border="1" style="width:100%">
                    <tbody>
                        <tr><td>타이틀</td><td>${schDatas[i].title}</td></tr>
                        <tr><td>링크</td>
                            <td><a href="${schDatas[i].link}">${schDatas[i].source.__text}</a></td>
                        </tr>
                        <tr><td>발행일</td><td>${schDatas[i].pubDate}</td></tr>
                    </tbody>
                </table>
                <br>
            </div>`
        }
        myDisp.innerHTML += itemStr;
        dataCnt++; 
    }

    function fInit(){
        //api.allorigins.win 무료라 느림!~~ ㅠㅠ
        let schVal = mySchWord.value;
        let googleUrl = `https://news.google.com/rss/search?q=${schVal}&hl=ko&gl=KR&ceid=KR:ko`;
        let url = `https://api.allorigins.win/get?url=${encodeURIComponent(googleUrl)}`;
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);
        xhr.onreadystatechange = function(){
            if (xhr.readyState == 4 && xhr.status == 200){
                //console.log( JSON.parse(xhr.responseText).contents);
                var jsonObj = x2js.xml_str2json( JSON.parse(xhr.responseText).contents );
                schDatas = jsonObj.rss.channel.item;
                console.log(schDatas);
                myDisp.innerHTML = "";
                fList();   
                window.scrollTo(0,0);
            }
        }
        xhr.send();
    }
    //fInit(); 테스토용
    //window.addEventListener("DOMContentLoaded",fInit);

    function fScroll(){
        let scrollTop = document.documentElement.scrollTop;
        let clientHeight = document.documentElement.clientHeight;
        let scrollHeight = document.documentElement.scrollHeight;
        let tunning = 50;

        if((scrollTop + clientHeight) >= (scrollHeight - tunning)){
            fList();
        }
    }
    window.addEventListener("scroll",fScroll);
</script>    
</body>
</html>

 

무한 스크롤을 꼬옥 위 처럼 바디영역이 확장되도록 사용하는 것은 아니당(window scroll)

DIV로도 많이 사용하는 데, overflow 속성을 이용하여, 부모 height를 고정시키고, 자식이 커지면

무한 스크롤 형태를 구현할 수 있당. 기본 흐름은 위와 완전히 동일하지만

scrollTop,clientHeight,scrollHeight 가져오는 부분이 쪼메 다르니 쭈목하장.

 

Div무한스크롤.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>무한루핑</title>
<style>
    #disp {
        border:10px groove black;
        width:600px; 
        height:600px;
        overflow: scroll;
    }
    .content {
        border:5px solid gold;
    }
</style>
<!-- xml <-> json 라이브러리 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/x2js/1.2.0/xml2json.min.js"></script>
</head>
<body>
    <h1>무료우회서버 사용해서 마니 느려용</h1>
    <input type="text"  id="schWord" value="" onkeydown="fEnter()"><button onclick="fSch()">껌색</button>
    <div id="disp">
    </div>
<script>
    const mySchWord = document.querySelector("#schWord");
    function fEnter(){
        if(event.keyCode == 13){
            dataCnt = 1;
            fInit();
        }
    }

    function fSch(){
        if(mySchWord.value.trim().length < 2){
            alert("검색어가 너무 짧아용");
            return;
        }
        dataCnt = 1;
        fInit();
    }

    const myDisp  = document.querySelector("#disp");
    const x2js = new X2JS();
    let schDatas;
    let dataCnt = 1;
    let perCnt = 10;
    let msgOpen  = false;  // 데이타 없다는 메세지 한번만 뜨겡!
    function fList(){
        let itemStr ="";
        let starIndex = (dataCnt -1) * perCnt;
       
        if(starIndex >= schDatas.length){
            if(msgOpen) return;
            alert("더 이상 데이터가 없어용 쏘링");
            msgOpen = true;
            return;
        }

        let endIndex = starIndex + perCnt;
        if(endIndex > schDatas.length){
            endIndex = schDatas.length;
        }

        for(let i=starIndex; i < endIndex ; i++){
            itemStr += `
            <div class="content">
                <table border="1" style="width:100%">
                    <tbody>
                        <tr><td>타이틀</td><td>${schDatas[i].title}</td></tr>
                        <tr><td>링크</td>
                            <td><a href="${schDatas[i].link}">${schDatas[i].source.__text}</a></td>
                        </tr>
                        <tr><td>발행일</td><td>${schDatas[i].pubDate}</td></tr>
                    </tbody>
                </table>
                <br>
            </div>`
        }
        myDisp.innerHTML += itemStr;
        dataCnt++; 
    }

    function fInit(){
        let schVal = mySchWord.value;
        let googleUrl = `https://news.google.com/rss/search?q=${schVal}&hl=ko&gl=KR&ceid=KR:ko`;
        let url = `https://api.allorigins.win/get?url=${encodeURIComponent(googleUrl)}`;
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);
        xhr.onreadystatechange = function(){
            if (xhr.readyState == 4 && xhr.status == 200){
                //console.log( JSON.parse(xhr.responseText).contents);
                var jsonObj = x2js.xml_str2json( JSON.parse(xhr.responseText).contents );
                schDatas = jsonObj.rss.channel.item;
                console.log(schDatas);
                myDisp.innerHTML = "";
                fList();   
                myDisp.scrollTo(0,0);
            }
        }
        xhr.send();
    }
   // fInit(); 테스토용


    //myDisp에 스크롤 이벤트가 걸리는 거시 다름
    function fScroll(){
        let scrollTop = myDisp.scrollTop;
        let clientHeight = myDisp.clientHeight;
        let scrollHeight = myDisp.scrollHeight;
        let tunning = 50;

        if((scrollTop + clientHeight) >= (scrollHeight - tunning)){
            fList();
        }
    }
    myDisp.addEventListener("scroll",fScroll);
</script>    
</body>
</html>

원리만 이해되었다면, jQuery로 하는 건 꺼미당. 구찌 필요없당.

 

소스에서 추가적으로 찝고 넘어가고 싶은 부분은 google News가 xml 문자열 형태로

오기 때문에, xml <=> json 변환 라이브러리x2js를 사용하였고,

 

google News(rss형식)가 Cross-Origin 플러그인을 설치해도 Cross-Origin 에러만 없어질 뿐

[실제론 서비스가 조금 불안정해서 마구 요청하면 발생할 수도 있음]

403에러로 데이타가 오지 않아, allOrigins에서 제공하는 무료 우회 서비스를 사용하였다.

allOrigins 서비스는 무료라 그런지 조금 느리지만, 서버쪽 프로그램 구성없이 

바로 사용할 수 있어, 많이 편하다. x2js와 allOrigins 둘 다 알아두도록 하장(도움된당!)

 

하드웨어 방면에서 신호가 왜곡되는 것을  스큐(skew-뒤틀림)라 하고, 이것을 바로 잡는 것을

디스큐(deskew)라 부르는 데, 드디어 기다리던 제대로 된 비가(슬픈노래) 온당.

나의  뇌파도 시간이 지남에  뒤틀림이 누적되어 커지는 데, 다행히 빗소리를 들으면

뒤틀림이 바로 잡히고, 조율되고, IQ와 EQ가 드디어 하나가 되는 느낌같은 느낌이당.

 

어려선 빗소리 들으면서  잠든 기억이 있는데, 요즘 창문은 방음이 너무 훌륭해서 

소리로는 비가 오는 지 알 수 없는 게 조금 마니 Lonely 하당.

나에겐 마치 피아노 조율사 같은 빗소리를 들으려  지금 빗속으로 들어간당.

컴퓨터가 살아있다면 RESET(리셋)될 때 이런 기분일깡?(부활!~~)  

 

시대가 컴퓨터와 인터넷 덕분에 느끼는 공간영역이 넓어지고, 거기에 따라 업무 영역도 

넓어지면서 커뮤니케이션 능력이 뛰어난 사람이 대우 받는 세상이 되었고, 앞으로

더욱 더 넓고 다양한 커뮤니이션 방법이 등장할 거시다.

헌데 난 아직 스마트폰 조차 잘 활용하지 못하고, 심지어 그 존재가 밉당.~~~

마일리 사이러스의 의견을 들어보장.

 

 

커뮤니케이션

https://www.youtube.com/watch?v=LJQVyvaQf-4 

 

관련글 더보기