본문 바로가기
웹 코딩/티스토리

퀵점프 - 본문용 책갈피? 목차? 바로가기?

by 알릭2 2020. 9. 2.

저와는 달리 글의 내용이 충실한 열정 블로거들을 위한 툴!? ㅋ

 

이름은 퀵점프! ㅋ

 

본문 내용 중 제목 문단1~3 (h1, h2, h3, h4) 을 썼거나 굵게<b> 태그로 표시한 내용이 있다면 뽑아서 바로 가기를 제공합니다.

 

글의 키워드 목록이라고나 할까요?

 

우선, wbluke 님의 ToC (table of contents) 목차 기능을 보고 영감을 받아 만들어 보았습니다.

계층적이고 디테일한 목차가 필요하신 분은 고고!

 

대신 저는 개인적으로 제목 문단을 잘 사용하지 않고, 글의 구성이 단층적이며, 흐름에 끊김이 없이 쓰면서 구간별 구분을 명확히 하는 그런 스타일이 아니라고 판단해 목차보다는 단순히 키워드 추출의 느낌으로 글 내용중 중요시 되어야 하는 부분으로 바로 이동하는 것을 주목적으로 구성했습니다.

 

설치법

1. 스킨 편집 > HTML > 하단 </body> 가 끝나기 직전 아래 내용 추가

<script>
//window.addEventListener("load", function() { 
	(function(d) {
		var article = d.querySelector('.tt_article_useless_p_margin');
		if (!article) { return; }
		var keywords = article.querySelectorAll('h1,h2,h3,.tt_article_useless_p_margin>h4,p>b,blockquote>b');
		var qjWrap = d.createElement('DIV'); qjWrap.id = 'qjWrap';
		var qjFix = d.createElement('DIV'); qjFix.id = 'qjFix';
		var qjHead = d.createElement('DIV'); qjHead.id = 'qjHead';		
		var qj = d.createElement('DIV'); qj.id = 'qj';
		var supportsNativeSmoothScroll = 'scrollBehavior' in document.documentElement.style;
		var cnt = 0;
		var timers = {};
		for (var i = 0; i < keywords.length; i++) {
			var keyword = keywords[i];
			var linkRow = d.createElement("div");
			var link = d.createElement("span");
			if (keyword.innerText.trim() != "") {
				cnt++;
				keyword.id = 'kw' + i;
				keyword.classList.add('kw');
				link.dataset.to = '#kw' + i;
				link.title = keyword.textContent;
				link.innerText = keyword.textContent;
				linkRow.appendChild(link);
				qj.appendChild(linkRow);
				link.addEventListener("click", function() {
					var kw = d.querySelector(this.dataset.to);
					var newTop = parseInt(offset(kw).top - 20);
					if(supportsNativeSmoothScroll) {
						window.scroll({ top: newTop, left: 0, behavior: 'smooth' });
					} else {
						if (document.currentScript === undefined) {
							window.setTimeout(function(){window.scrollTo(0, newTop);},1);
						} else {
							d.body.scrollTop = newTop;
						}
					}
					var preKw = d.querySelector('.kw.outline');
					if (preKw) {
							preKw.classList.remove('outline');
							if (timers[preKw.id.slice(2)]) {clearInterval(timers[preKw.id.slice(2)]);}						
					}
					kw.classList.add('outline');
					if (d.currentScript === undefined) {
						window.setTimeout(function() {kw.classList.remove('outline')}, 3000);					
					} else {
						var timesRun = 0;
						if (timers[i]) {clearInterval(timers[i]);}
						timers[i] = setInterval(function(){
							timesRun += 1;
							if(timesRun === 7){
								clearInterval(timers[i]);
							}
							kw.classList.replace((kw.classList.contains('outline'))?'outline':'nooutline',(kw.classList.contains('outline'))?'nooutline':'outline');
						}, 500); 						
					}
				});
			}
		}
		if (cnt > 0) {
			qjFix.appendChild(qjHead);
			qjFix.appendChild(qj);
			qjWrap.appendChild(qjFix);
			article.style.position = 'relative';
			article.insertBefore(qjWrap, article.childNodes[0]);
			window.addEventListener('scroll', function() {
				var scrollY = this.pageYOffset;
				var windowH = document.documentElement.clientHeight;
				var cutLine = scrollY + (windowH * 0.4);
				var kws = d.querySelectorAll('.kw');
				for (var i = 0; i < kws.length; i++) {
					var kw = kws[i],
							kwTop = offset(kw).top;
					if (kwTop >= scrollY && kwTop <= cutLine) {
						d.querySelector('[data-to="#' + kw.id + '"]').classList.add('now');
					} else {
						d.querySelector('[data-to="#' + kw.id + '"]').classList.remove('now');
					}
				}
				var now = d.querySelector('#qj span.now');
				var qj = d.querySelector('#qj');
				if (now) {
					var newTop = parseInt(now.offsetTop - (qj.offsetHeight / 2) - (now.offsetHeight / 2));
					if(supportsNativeSmoothScroll) {
						qj.scrollTo({
							top: newTop,
							left: 0,
							behavior: 'smooth'
						});
					} else {
							qj.scrollTop = newTop;
					}
				}
				var endPos = offset(article).top + article.offsetHeight;
				if (endPos+20 <= scrollY + windowH) {
					d.querySelector('#qjWrap').style.display = 'none';
				} else {
					d.querySelector('#qjWrap').style.display = 'block';
				}
			});
		}
	})(document);
	function offset(el) {
		var rect = el.getBoundingClientRect(),
				scrollLeft = window.pageXOffset || document.documentElement.scrollLeft,
				scrollTop = window.pageYOffset || document.documentElement.scrollTop;
		return { top: rect.top + scrollTop, left: rect.left + scrollLeft }
	}
//});
</script>

2. 아래 스타일을 스킨의 CSS 에 추가

#qjWrap {
  position: absolute;
  z-index: 3;
  right: -54px; /*본문과의 간격 조정 및 노출 위치 지정*/
}

#qjFix {
  position: fixed;
  width: 120px; /*퀵점프 넓이 지정*/
  font-family: sans-serif, monospace;
}

#qjHead {
  width: 100%;
  padding: 2px 0;
  background: darkgray;
  text-align: center;
  font-size: 13px;
  color: white;
}

#qjHead::before {
	content: 'Quick Jump';
}

#qj::-webkit-scrollbar {
	display: none;
}

#qj {
  -ms-overflow-style: none;
  scrollbar-width: none;
}

#qj {
  box-sizing: border-box;
  overflow-x: hidden;
  overflow-y: auto;
  width: 100%;
  max-height: 40vh;
  border: 1px solid #ccc;
  background: white;
  color: #999; /*퀵점프 글 색상 지정*/
}

#qj div {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-size: 12px;
  margin: 2px 0;
  padding: 2px 4px;
}

#qj div span:hover {
  cursor: pointer;
  text-decoration: underline;
}

#qj div span.now {
  color: black; /*퀵점프 선택된 항목 색상 지정*/
  font-weight: bold;
}

.kw.outline {
	outline: 1px solid #bbb; /*퀵점프 클릭 후 이동한 대상 항목 테두리*/
}

추가 설명

위 설치 과정 진행 중 퀵점프의 노출 위치를 잡아줍니다. (css 의 주석참고)

- 노출 위치가 왼쪽인 경우 right: -54px 를 left: -174px 로 변경

(-174는 넓이 120과 간격 54 를 더해 산출됨, 간격은 필요에 따라 조정 필요)

 

키워드로 추출되는 항목은 현재 아래와 같습니다. (소스 5번줄 참고)

'h1, h2, h3, .tt_article_useless_p_margin>h4, p>b, blockquote>b'

(h1, h2, h3, 본문 직속 h4, 문단 직속 b, 인용구 직속 b 태그)

본인의 글쓰는 스타일에 따라 불필요한 내용이 있거나, 추가할 내용이 있다면 이 부분 태그 목록을 수정하시면 됩니다.

 

소스 내용중 0.4 가 있는데, 이는 화면의 상단 40%에 키워드가 위치하면 퀵점프 박스 안에서 강조시킨다는 의미입니다. 필요에 맞춰 조정하시면 됩니다.

 

페이지 로딩이 다 끝나고 보여주고 싶다면 소스의 첫줄과 끝줄의 주석 // 을 제거하시면 됩니다.

 

참고로 추출된 키워드가 없으면 퀵점프 박스는 나타나지 않습니다.

 

기타

  • 스크롤이 본문을 넘어 내려가면 퀵점프 박스는 잠시 사라집니다.
  • 키워드 목록이 길어도 퀵점프 박스 내에서 스크롤 되기 때문에 전혀 문제 없습니다.
  • IE11과 edge 에서도 잘 작동합니다만 부드러운 스크롤은 지원되지 않습니다.
  • jQuery 를 썼으면 코드가 많이 짧아질텐데.. 혹시 몰라 순수 자바스크립트로만 했습니다.

문의 및 버그 신고는 늘 환영입니다.

댓글