Dev_articles/General

[Blog] 티스토리에 자동 목차(TOC) 추가하기 feat. Odyssey skin

humblEgo 2021. 1. 2. 12:45

Odyssey skin tistory 블로그에 TOC를 추가하는 방법을 안내합니다. Odyssey 스킨 유저라면 복붙만 잘 하시면 됩니다🤗
(2021년 현재 블로그 스킨은 #1으로 변경된 상태입니다. 참고해주세요!) 


들어가며

티스토리 블로그를 만들면서 가장 넣고 싶었던 기능이 있었는데, 바로 '본문 옆에 삽입되는 소제목 목차 기능'이었습니다. 보통 TOC(Table Of Contents) 기능이라고 부릅니다. TOC가 있으면 글의 구조를 한 눈에 볼 수 있고, 목차를 누르면 필요한 내용을 바로 찾아갈 수 있습니다. 글을 읽는 입장에서는 목차가 없는 것보다 훨씬 좋은 경험을 하게 되죠.

 

티스토리에 TOC를 적용하려면 직접 스킨을 수정해줘야합니다. 검색해보면 친절하게 코드를 복붙만 하면 바로 적용가능하게끔 잘 설명된 예시를 많이 찾을 수 있습니다.

 

문제는 티스토리 스킨들의 DOM 구조가 제각기 다른데, #1 스킨, 북클럽 스킨 그리고 포스터 스킨에 TOC를 적용한 예시들이 대부분이라는 것입니다. 때문에 제 티스토리 스킨인 'Odyssey' 스킨에 바로 복붙해서 적용할 순 없었습니다 😢 아마 저처럼 Odyssey 스킨의 사이드바 형태를 '2단 우측 사이드바' 대신 '1단 서랍 사이드바'로 쓰시는 분이 적어서 예시가 없는 것 같아요.

velog에서는 글 쓰면 알아서  TOC 가 삽입됐었는데..!

다행히 반나절 간의 시행착오 끝에 Tocbot을 이용해서 TOC를 추가할 수 있었습니다. Odyssey 스킨 유저 분도 복붙만하면 바로 적용가능하게끔 정리해서 공유드립니다!


Toc 적용하기

1) html 편집창 열기

블로그 관리에 들어가서 '스킨 편집'을 클릭하고, 이어서 'html 편집'을 클릭합니다.

블로그 관리 -> 스킨 편집 -> html 편집


2) head 영역에서 Tocbot 로드

Tocbot은 Tocify와 비슷하지만 jQuery 의존성을 제거했다는 점이 다릅니다. 즉, jQuery를 다운로드하고 업로드할 필요가 없다는 뜻이죠!

 

바로 HTML의 <head>와 </head> 사이에 아래 코드를 복사하여 붙여줍니다. 스크린샷을 참고하세요.

<!-- tocbot --> 
<script src="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.11.1/tocbot.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.11.1/tocbot.css">
<!-- tocbot --> 

저는 </head> 바로 위에 삽입했습니다.


3) body 영역에 TOC 요소 삽입

이제 아래 코드를 <body>와 </body> 사이에 있는 <div class="article-view"> 태그 위에 복붙해줍니다. 본문이 시작되는 부분에 넣은 셈입니다. 프론트엔드 쪽 지식이 아직 부족하다보니 이 위치가 최적인지는 모르겠네요, 아시는 분은 댓글로 알려주시면 감사히 배우겠습니다 😀

 

<!-- toc -->
<div class='toc toc-fixed'></div>
<!-- toc -->

article-header와 article-view 사이에 삽입


4) body 영역 끝에 TOC 랜더링 script 삽입

이제 body 영역의 끝, 즉 </body> 태그 바로 위에 아래 코드를 삽입해줍니다. 코드는 pwnbit님의 블로그를 참고하였습니다. 아직 javascript를 다뤄본 적이 없다보니 눈치껏(..) 수정했는데 잘 작동하네요.

<!-- toc script start-->
<script> 
	// set heading id 
	function makeHeading(headings){ 
		Array.prototype.forEach.call(headings, function (heading) { 
			var id = heading.id ? heading.id : heading.textContent.trim().toLowerCase() .split(' ').join('-').replace(/[\!\@\#\$\%\^\&\*\(\)\:]/ig, '') 
			headingMap[id] = !isNaN(headingMap[id]) ? ++headingMap[id] : 0; 
			if (headingMap[id]) { heading.id = id + '-' + headingMap[id]; 
													} else { 
														heading.id = id; } 
		}); 
	} 
	var headingMap = {}; 
	var headings = document.querySelector('.article-view').querySelectorAll('h1, h2, h3');
	makeHeading(headings); 
	document.addEventListener("DOMContentLoaded", function() {
		// toc addon
	tocbot.init({ 
		// Where to render the table of contents. 
		tocSelector: '.toc', 
		// Where to grab the headings to build the table of contents.
		contentSelector: '.article-view', 
		// Which headings to grab inside of the contentSelector element. 
		headingSelector: 'h1, h2, h3', 
		// For headings inside relative or absolute positioned containers within content. 
		hasInnerContainers: false
		}); 
	});
	
	$("div.toc.toc-fixed").hide(0);
	$(window).scroll(function() { 
		if (Math.round( $(window).scrollTop()) > 400) { 
					$("div.toc.toc-fixed").show(250);
		} else { 
					$("div.toc.toc-fixed").hide(250);
		} 
	}); 
</script>
<!-- toc script end-->

</body> 위에 삽입

</body> 위에 삽입하는 이유는 body의 다른 요소가 모두 랜더링된 다음 script를 적용시키기 위함이라고 합니다. 다소 투박하게 느껴지는데 추후 기회가 된다면 다른 세련된 방법이 있는지 확인해봐야겠습니다.


5) CSS 삽입

이제 마지막으로 아래 코드를 CSS의 제일 밑에 삽입해줍니다. 이 코드로 TOC가 랜더링되는 위치, 목차의 크기 등을 지정하게 되는데요, 아래 설정대로 복붙할 경우 이 블로그와 동일한 TOC를 얻으실 수 있습니다. 입맛대로 수정해보세요 🤗

/* tocbot */ 
.toc-absolute { 
	position: absolute; 
	margin-top: 165px; 
}

.toc-fixed { 
	position: fixed;
	top: 165px; 
} 

.toc { 
	left: calc((100% - 720px) / 2 - 300px);
	width: 250px;
	padding: 10px;
	box-sizing: border-box; 
	z-index: 0;
} 

.toc-list { 
	margin-top: 10px !important; 
	font-size: 0.9em; 
} 

.toc > .toc-list li { 
	margin-bottom: 10px;
} 

.toc > .toc-list li:last-child { 
	margin-bottom: 0; 
} 

.toc > .toc-list li a { 
	text-decoration: none; 
}

CSS파일 가장 밑에 삽입


참고