ioerror

[jQuery] 티스토리 목차(TOC) 만들기 - 2 본문

JavaScript

[jQuery] 티스토리 목차(TOC) 만들기 - 2...

반응형

상황

"티스토리에 내용 목차 만들기"에서 만든 목차(TOC) 스크립트에 스크롤 이동으로 목차에 현재 위치를 표시하는 기능을 추가하고 싶어 졌다.

scrollspy를 간단히 구현해서 적용하면 될 듯해서 스크립트와 스킨의 CSS 도 수정했다.

그리고 코드를 삽입했을 때 어떤 코드인지 코드 블록에 표시하는 것이 좋을 듯해서 스크립트를 추가했다.

 

스크롤 스파이 (scrollspy)

스크롤 위치를 감지해서 특정 기능을 수행하는 기능으로 bootstrap의 scrollspy를 사용하려 했으나 이 스킨에 부트스트랩을 적용하면 배보다 배꼽이 커질 거 같다.

일단 $(window).scroll 이벤트로 스크롤바의 위치를 확인하고 chapter.js 에서 생성된 <a name='name'> 태그의 위치를 확인, name에 해당하는 목차의 링크를 찾아서 스크롤이 해당 목차에 위치한다고 표시를 해주는 스타일 클래스 적용한다.

 

코드

$(function() {
	// 목록의 제목(h2,h3,h4 등) 배열
    let chapters = [];
    if ($('.chapters, .area_view h2, .area_view h3, .area_view h4').length > 0) {
        $('.chapters, .area_view h2, .area_view h3, .area_view h4').each(function(k, o) {
            $this = $(this);
            let text = $this.text();
            let depth = $this.data('chapters_depth');
            depth = typeof(depth) == 'undefined' ? 0 : depth;
            if (depth == 0) {
                if ($(this).prop('tagName') == 'h3') {
                    depth = 1;
                }
                if ($(this).prop('tagName') == 'h4') {
                    depth = 2;
                }
            }
            let padding_left = depth * 8;
            padding_left = padding_left == 0 ? 3 : padding_left;
            let css_indent = '';
            css_indent = " style='padding-left:" + padding_left + "px' ";
            let regExp = /[\{\}\[\]\/?.,;:|\)*~`!^\+<>@\#$%&\\\=\(\'\"]/gi; // 占쎈��붻눧紐꾩쁽 -占쏙옙 占쎌뮇��
            let text_esc = text.replace(/\&lt\;/g, '<').replace(/(\&gt\;)/g, '>');
            text_esc = text_esc.replace(/<[^>]+>/gm, '');
            text_esc = text_esc.replace(/\&nbsp\;/g, '_').replace(regExp, '').replace(/ /g, '_');
            text_esc = text_esc + '_' + k;
            $this.data('chapter-key', k);
            $target_anchor = $('<a name="' + text_esc + '" class="chapters-target"></a>');
            $this.before($target_anchor);
            depth = depth > 3 ? 3 : depth;
            $('#chapters_lists > dl').append("<dd ><a href='#" + text_esc + "' class='chapters-item chapters-item-depth-" + depth + "' " + css_indent + ">" + text + "</a></dl>");
            // 제목 태그를 배열에 저장한다.
            chapters.push($target_anchor);
        });
        $('#chapters_lists').addClass("chapters_lists_on");
        $('.area_title').after("<div class='area_chapters'>" + $('#chapters_lists').html() + "</div>");

		// 목차를 클릭하면 스크롤스파이 클래스 적용
        $(document).on("click", ".chapters-item", function() {
            $(this).parent().addClass('chapter-on-scroll');
            let chapter_anchor = $(this).prop("href").replace('#', '');
            $("#chapters_lists a[href='#" + chapter_anchor + "']").parent().addClass('chapter-on-scroll');
            $("#chapters_lists a").not("a[href='#" + chapter_anchor + "']").parent().removeClass('chapter-on-scroll');
        });
		// 스크롤 스파이 부분
        $(window).scroll(function(event) {
            let scroll_y = $(this).scrollTop();
            let chapter_anchor;
            if (chapters.length == 0) {
                return;
            }
            // 스크롤되면 제목 태그 중에서 스크롤 위치보다 작은 것 중 제일 큰 것의 위치를 확인 
            for (k in chapters) {
                let chpt = $(chapters[k]);
                try {
                    if (scroll_y >= chpt.offset().top) {
                        chapter_anchor = chpt.prop("name");
                    }
                } catch (e) {
                    break;
                }
            }
            // 스크롤 위치 알림 스타일 적용
            $("#chapters_lists a[href='#" + chapter_anchor + "']").parent().addClass('chapter-on-scroll');
            $("#chapters_lists a").not("a[href='#" + chapter_anchor + "']").parent().removeClass('chapter-on-scroll');
        });
    }


	// 덧붙여 코드 블록에 언어명을 표기하기
    // 티스트로에 쉘스크립트가 없어서 사용안하는 SCALA를 Shell 로 적용
    if ($("pre[data-ke-type='codeblock']").length > 0) {
        $("pre[data-ke-type='codeblock']").each(function() {
            let pre_lang = $(this).data('ke-language').toUpperCase();
            pre_lang = pre_lang == 'SCALA' ? 'SHELL' : pre_lang;
            $(this).prepend("<span class='codeblock-lang'>" + pre_lang + "</span>");
        })
    }
});

chapters.js
0.00MB

반응형
Comments