정보/블로그 운영팁

티스토리 떠 다니는 플로팅 목차 만드는 방법 (고급 버전)

아미넴 2020. 12. 20.
반응형

초급 버전에서 좀 더 기능을 강화한 버전을 만들어 보았습니다.

어느 정도 코딩이 가능한 분이어야 수월하게 따라하실 수 있을 겁니다.

 

이 부분이 어렵게 느껴지시는 분은 다음 초급 버전을 참고 바랍니다.

사실 난이도는 큰 차이가 없습니다.. ^^;

 

주의) 초급 버전과 내용이 중복 되므로 이전 반영 사항이 있다면 먼저 제거하셔야 합니다.

참고) 이 포스팅은 북클럽 스킨 기반이라 다른 스킨에서 정상적인 동작이 이루어지지 않을 수 있어요.

댓글로 사용하시는 스킨 알려 주시면 보완하여 내용을 추가하도록 하겠습니다.

 

티스토리 떠 다니는 플로팅 목차 만드는 방법 (초급 버전)

 

티스토리 떠 다니는 플로팅 목차 만드는 방법 (초급 버전)

목차를 확인하려면 다시 위로 올라가야 하는 불편함을 해소하고자 만들어 보았습니다. 생각보다 깔끔하게 잘 만들어진 것 같아서 공유합니다. 목차 목차 자동 생성 먼저 목차가 생성되어 있어

sangminem.tistory.com

 

목차

     

    목차 자동 생성

    먼저 목차가 생성되어 있어야겠죠 ㅎㅎ

    이전에 포스팅 한 내용이 있으므로 링크로 대체 하겠습니다.

     

    티스토리 글에 자동으로 목차 넣기

     

    티스토리 글에 자동으로 목차 넣기

    목차를 넣고 싶긴한데 글 쓸 때마다 매번 수작업으로 만든다면 상당히 번거롭겠죠. 그래서 jQuery 플러그인 Table of Contents(TOC)를 이용하여 자동으로 넣는 방법을 소개합니다. 저는 제목1, 제목2로

    sangminem.tistory.com

    목차부터 적용하시고 다음 단계를 진행하시면 됩니다.

     

     

    HTML 태그 작성

    먼저 태그 하나를 작성할 건데요.

     

    관리 페이지 > 꾸미기 > 스킨 편집 으로 이동합니다.

     

    html 편집을 클릭합니다.

    그럼 위외 같은 HTML 에디터가 뜨는데 여기에서 작업을 해 보겠습니다.

     

    body 태그 내에 적당한 위치에 다음과 같이 작성하세요.

    <!-- 플로팅 목차 -->
    <div class="floating-toc"></div>

    이 태그가 나중에 목차로 떠 다니는 부분입니다.

     

     

    CSS 작성

    다음 스타일을 작성해 보겠습니다.

     

    CSS 탭으로 이동합니다.

     

    아래와 같이 작성하세요.

    .floating-toc {
        position: absolute;
        cursor: pointer;
        border: 1px solid #b0d197;
        background: #fff;
        padding: 10px;
        z-index: 999;
        display: none;
    }
    .floating-toc:after {
        content:"";
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: #b0d197;
        opacity: 0.1;
        z-index: -1;
    }
    .floating-toc p {
        font-weight: bold;
        font-size: 1em !important;
        margin: 10px 0 10px 0;
    }
    .floating-toc #toc-body {
        margin-top: 10px;
    }
    .floating-toc #toc-body #toc * {
        font-size: 15px;
    }
    .floating-toc #toc-body #toc {
        margin-left: 0;
        padding-left: 20px;
    }
    .floating-toc #toc-body #toc > li > ul > li > a {
        font-size: 0.9em;
    }
    .floating-toc #toc-body #toc > li > ul > li > ul > li > a {
    	font-size: 0.8125em;
    }
    .floating-toc-ani {
        animation: bounce-toc 5s linear infinite;
    }
    @keyframes bounce-toc {
        0% {
            transform: scale(1.0);
        }
        50% {
            transform: scale(0.9);
        }
        100% {
            transform: scale(1.0);
        }
    }

    그냥 플로팅 목차를 꾸미기 위한 부분이고 참고 사항으로 제공한 코드이므로 특별히 설명은 하지 않겠습니다.

    본인의 취향에 맞게 꾸미시면 됩니다.
    floating-toc 클래스의 position을 absolute로 하게 되면 스크롤 좌표에 영향을 받고 fixed로 하면 화면 상 보이는 위치 만큼만 영향을 받는다는 것에만 유의하시면 될 것 같습니다.

     

     

    자바스크립트 작성

    지금부터 목차가 따라다니는 로직을 만들어 보겠습니다.

     

    body 태그 안쪽 맨 아래 > script 태그 안쪽 작성하시면 됩니다.

    스크롤에 따른 목차 이동 함수 작성

    다음과 같이 작성하겠습니다.

    var safeFlag = true;
    function appendToc() {
        var bookToc = $('.book-toc');
        var floatingToc = $('.floating-toc');
        if(bookToc.length > 0 && window.scrollY > bookToc.offset().top + bookToc.innerHeight()) {
            if(floatingToc.height() === 0 && bookToc.innerHeight() > 70 && safeFlag) {
                safeFlag = false;
                floatingToc.css('display','block');
                bookToc.css('width',bookToc.width()+'px'); //목차 박스 크기에 이상 있으면 제거하세요
                bookToc.css('height',bookToc.height()+'px'); //목차 박스 크기에 이상 있으면 제거하세요
                var tocTitle = $('<div id="toc-title"><p>목차 <span style="opacity:0.5;">펼치기</span></p></div>');
                floatingToc.append(tocTitle);
                bookToc.css('height', bookToc.height()); //목차 박스 크기에 이상 있으면 제거하세요
                var tocBody = $('<div id="toc-body" style="display:none;"></div>').append(bookToc.find('#toc'));
                floatingToc.append(tocBody);
                
                floatingToc.addClass('floating-toc-ani');
                
                floatingToc.css('padding', '0');
                $('#toc-title').css('padding', '10px');
                
                floatingToc.css('top', "20px");
                floatingToc.css('left', "20px");
                floatingToc.css('position', "fixed");
                
                // 목차 클릭
                function clickTitle() {
                    floatingToc.css('transition', '');
                    var title = $('#toc-title>p>span');
                    if(title.text() === "펼치기") {
                        $('#toc-title').css('padding', '10px 0 0 0');
                        floatingToc.css('padding', '0px 10px 10px 10px');
                        floatingToc.removeClass('floating-toc-ani');
                    }
                    
                    setTimeout(function(){
                        $('#toc-body').slideToggle(300,'linear', function() {
                            if(title.text() === "접기") {
                                floatingToc.css('transition', '');
                                title.text("펼치기");
                                floatingToc.css('padding', '0');
                                $('#toc-title').css('padding', '10px');
                                floatingToc.addClass('floating-toc-ani');
                            } else {
                                title.text("접기");
                            }
                        });
                    },200);
                }
                $('#toc-title').on('click', function(){
                    clickTitle();
                });
                
                //목차 항목 클릭
                $('#toc-body').find('a').on('click', function(){
                    setTimeout(function(){
                        checkPosition();
                    }, 200);
                });
                
                clickTitle();
                setTimeout(function(){
                    if($('.tt_article_useless_p_margin').offset().left < $('.floating-toc').width() + 40) {
                        clickTitle();
                    }
                }, 500);
                
                setTimeout(function(){
                    safeFlag = true;
                }, 1100);
            } else {
                checkPosition();
            }
        } else {
            $('#toc-title').off('click');
            bookToc.append(floatingToc.find('#toc'));
            floatingToc.find('div').remove();
            floatingToc.removeAttr('style');
            floatingToc.css('display','none');
            $('#toc').find('a').removeAttr('style');
        }
    }

    스크롤 한 Y좌표 위치 페이지의 맨 상단에서부터 목차가 있는 곳까지의 거리 + 목차 박스 크기만큼의 길이보다 커지면 원래 목차 위치에서 떼어내어 위에서 작성했던 div로 이동시켜서 스크롤 할 때마다 고정적으로 보이도록 하였습니다.

    목차 전체가 보이게 하면 화면의 너무 많은 자리를 차지하므로 목차를 펼치고 접을 수 있도록 클릭 이벤트를 연결하였습니다.

    또한 왼쪽 공간이 충분히 넓으면 처음 플로팅 목차가 보여질 때 펼쳐진 상태가 되도록 하였습니다.

    jQuery에서 제공하는 slideToggle 메서드를 사용하여 애니메이션 효과를 부여하고 접기/펼치기 텍스트를 바꿔 주었습니다.

    그리고 스크롤을 하거나 목차의 제목을 클릭하면 이동하는 부분이 어느 위치인 지를 나타내기 위한 함수도 추가하였는데 아래에 이어서 작성하도록 하겠습니다.

    스크롤이 원래 목차있던 위치로 복귀하면 플로팅 목차는 사라지게 됩니다.

     

    작성하다 보니 상당히 복잡하게 되었는데요.

    제 블로그에서 보고 목차 기능을 만드셨다면 이 함수도 거의 그대로 가져다 쓰시면 될 것 같습니다.

    +내용 추가 (2020.01.02)

    공간이 좁은 모바일에서 목차가 접혔을 때 아이콘으로 표시하는 방법입니다.

    먼저 css에 클래스 하나를 추가합니다.

    .floating-icon:after {
        font-family: "Font_Awesome_5_Free";
        content: "\f03a";
        font-size: 0.8125;
        color: #548a25;
    }

    Font Awesome 5 Free 글꼴을 모르시면 다음 포스팅을 참고하세요.

    티스토리 목차 글머리 서식 변경하기

     

    티스토리 목차 글머리 서식 변경하기

    기본 글머리 기호도 심플하긴 하지만 원하는 모양으로 바꾸면 더 좋을 것 같다는 생각을 했습니다. 기본 글머리 설정 방법 list-style-type - CSS: Cascading Style Sheets | MDN The list-style-type CSS proper..

    sangminem.tistory.com

     

    그리고 다음과 같이 수정합니다.

    // 이 부분을 수정합니다.
    var tocTitle = $('<div id="toc-title"><p>목차 <span style="opacity:0.5;">펼치기</span></p></div>');
    /***************************************************************/
    
    // 다음은 수정한 후 입니다.
    var tocTitle = $('<div id="toc-title"><div class="floating-icon"></div><p>목차 <span style="opacity:0.5;">펼치기</span></p></div>');

    아이콘을 표시할 div를 추가해 주었습니다.

     

    바로 아래에 다음을 추가합니다.

    //목차 아이콘으로 표시
    if(window.innerWidth < 768) {
        tocTitle.find('p').css('display','none');
    } else {
        tocTitle.find('.floating-icon').css('display','none');
    }

     

    마지막으로 clickTitle 함수를 수정합니다.

    // 목차 클릭
    function clickTitle() {
        floatingToc.css('transition', '');
        var title = $('#toc-title>p>span');
        if(title.text() === "펼치기") {
            $('#toc-title').css('padding', '10px 0 0 0');
            floatingToc.css('padding', '0px 10px 10px 10px');
            floatingToc.css('opacity', '1.0');
            floatingToc.removeClass('floating-toc-ani');
        }
        setTimeout(function(){
            $('#toc-body').slideToggle(300,'linear', function() {
                if(title.text() === "접기") {
                    floatingToc.css('transition', '');
                    title.text("펼치기");
                    floatingToc.css('padding', '0');
                    $('#toc-title').css('padding', '10px');
                    floatingToc.css('opacity', '0.5');
                    floatingToc.addClass('floating-toc-ani');
                    //목차 아이콘으로 표시
                    if(window.innerWidth < 768) {
                        tocTitle.find('p').css('display','none');
                        tocTitle.find('.floating-icon').css('display','block');
                    }
                } else {
                    title.text("접기");
                    //목차 아이콘으로 표시
                    if(window.innerWidth < 768) {
                        tocTitle.find('p').css('display','block');
                        tocTitle.find('.floating-icon').css('display','none');
                    }
                }
            });
        },200);
    }

    목차에 의한 게시물 방해를 최소화 하기 위해 투명도도 살짝 주었습니다.

     

    목차가 접혔을 경우 크기를 70~80%로 줄였습니다.

    @keyframes bounce-toc {
        0% {
            transform: scale(0.8);
        }
        50% {
            transform: scale(0.7);
        }
        100% {
            transform: scale(0.8);
        }
    }

    수치를 조금씩 변경해 보면서 원하는 대로 줄이면 될 것 같습니다.

     

    +내용 추가 (2021.01.03)

    모바일에서 접었다 펼쳐지는 행위가 보기 산만하다는 의견을 반영하여 전체 로직은 흔들지 않으면서 깔끔하게 처리할 수 있는 방법을 생각해 보았습니다.

    // 이 부분을 수정해 보겠습니다.
    clickTitle();
    setTimeout(function(){
        if($('.tt_article_useless_p_margin').offset().left < $('.floating-toc').width()+20) {
            clickTitle();
        }
    }, 500);
    setTimeout(function(){
        safeFlag = true;
    }, 1100);
    
    /*****************************************************************************/
    
    // 수정 후 소스입니다.
    floatingToc.css('visibility', 'hidden'); // visibility 값을 hidden으로 주어 잠시 숨김
    clickTitle();
    setTimeout(function(){
        if($('.tt_article_useless_p_margin').offset().left < $('.floating-toc').width()+40) {
            clickTitle();
        }
    }, 500);
    setTimeout(function(){
        floatingToc.css('visibility', ''); // 완전히 접힌 후 다시 보이도록 함
        safeFlag = true;
    }, 1100);

    기존 로직은 거의 영향을 주지 않고 반영하였습니다. 궁금하신 점은 댓글로 말씀 부탁드릴게요.

     

     

    현재 항목 강조 기능 추가

    현재 화면 상에 나타는 내용이 어느 위치인 지를 나타내기 위한 checkPosition 함수입니다.

    function checkPosition() {
        var titleList = $('.tt_article_useless_p_margin').find('h2,h3,h4');
        var scrollY = window.scrollY + 50;
        if(titleList.length > 1) {
            for(var i=0; i < titleList.length; i++) {
                var tocList = $('#toc').find('a');
                if(i < titleList.length-1) { //다른카테고리 글보기 플러그인을 사용하신다면 length-2로 바꿔주세요
                    if(titleList.eq(i).offset().top < scrollY && titleList.eq(i+1).offset().top > scrollY) {
                        tocList.removeAttr('style');
                        tocList.eq(i).css('font-weight','bold');
                        break;
                    }
                } else {
                    if(titleList.eq(i).offset().top < scrollY) {
                        tocList.removeAttr('style');
                        tocList.eq(i).css('font-weight','bold');
                        break;
                    }
                }
            }
        }
    }

    현재 어느 위치에 있는지 파악하여 목차에 bold 스타일로 강조를 하였습니다.

     

     

    스크롤 이벤트 추가

    간단히 다음과 같이 작성하면 됩니다.

    /* DOM 로드 직후 실행 */
    $(document).ready(function() {
        /* 스크롤 이벤트리스너 */
        $(window).on('scroll', function() {
            appendToc();
        });
    });

    위에서 작성한 함수를 스크롤 이벤트에 등록하였습니다.

     

     

    결과 확인

    목차를 벗어나면 다음과 같은 플로팅 목차가 나타납니다.

     

    공간이 좁으면 접혀진 상태로 시작됩니다.

     

    클릭하면 다음과 같이 목차가 펼쳐집니다.

     

    영상으로도 확인해 보겠습니다.

     

    먼저 옆 공간이 충분할 때 영상입니다.

    펼쳐진 상태로 유지가 됩니다.

    물론 접을 수도 있습니다.

    그리고 스크롤을 내릴 때마다 현재 항목이 강조가 되는 것도 확인하실 수 있습니다.

     

    두번째로 옆 공간이 목차보다 좁을 때 영상입니다.

    플로팅 목차가 자동으로 접히게 됩니다.

    필요할 때 펼치시면 됩니다.

     

    모두 잘 작동하는군요.

     

    궁금하신 점은 댓글로 남겨 주시구요.

    저는 이만 물러갑니다 :)

    반응형

    댓글

    💲 추천 글