이번에는 플로팅 목차를 드래그 앤 드롭으로 원하는 위치로 이동시키는 기능을 만들건데요.
화면이 좁은 모바일에서 공간을 좀 더 효율적으로 활용하고자 생각하게 되었습니다.
목차가 계속 진화하고 있습니다.
목차 하나로 이렇게 다양한 컨텐츠가 나올 줄은 저도 미처 몰랐습니다 ㅎㅎ
어느새 제 블로그에 블로그 꾸미기가 주요 컨텐츠로 자리 잡았네요.
그럼 시작해 보겠습니다!
목차
플로팅 목차 적용
앞서 플로팅 목차를 적용하지 않으신 분은 다음 포스팅을 참고 바랄게요.
티스토리 떠 다니는 플로팅 목차 만드는 방법 (초급 버전)
티스토리 떠 다니는 플로팅 목차 만드는 방법 (고급 버전)
이번에 알려드리는 것이 목차의 끝판왕이라고 보셔도 될 것 같습니다 ㅋㅋ
필요 라이브러리 및 플러그인 소개
먼저 드래그 앤 드롭 기능을 직접 구현할 수도 있겠지만 상당히 힘들 것으로 예상되므로 라이브러리를 가져다 쓰도록 하겠습니다.
그런데 모바일에서 이 라이브러리를 이용하면 제대로 동작하지 않습니다.
따라서 라이브러리를 보완해 주는 플러그인을 하나 더 소개하겠습니다.
jQuery UI 라이브러리
jQuery에서 여러 효과와 컴포넌트, 위젯 등을 모아 놓은 라이브러리인데요.
저도 잘 사용하지는 않지만 드래그 앤 드롭 기능은 정말 유용합니다.
다음은 라이브러리 CDN 제공 사이트입니다.
이 라이브러리를 사용하여 우리가 원하는 기능을 구현해 보겠습니다.
jQuery UI Touch Punch 플러그인
PC에서 마우스를 이용하면 클릭 이벤트가 발생하고 모바일 기기에서는 터치 이벤트가 발생하는데요.
하지만 jQuery UI 라이브러리는 터치 이벤트를 고려하지 않고 있어서 이 플러그인이 필요합니다.
다음은 플러그인 CDN 제공 사이트입니다.
이 플러그인은 jQuery UI에서 터치 이벤트를 지원하도록 해 줍니다.
자바스크립트 수정
관리 페이지 > 꾸미기 > 스킨 편집 으로 이동합니다.
html 편집을 클릭합니다.
그럼 위외 같은 HTML 에디터가 뜨는데 여기에서 작업을 해 보겠습니다.
body 태그 안쪽 맨 아래 > script 태그 안쪽 작성하시면 됩니다.
어차피 플로팅 목차를 적용한 후에 이 포스팅을 보셔야 하므로 이 부분에 이미 코드가 작성되어 있어야 합니다.
드래그 앤 드롭 기능 적용
기존 appendToc 함수를 수정해 보겠습니다.
function appendToc() {
// 기존로직 생략
if(bookToc.length > 0 && window.scrollY > bookToc.offset().top + bookToc.innerHeight()) {
if(floatingToc.height() === 0) {
// 기존 로직 생략
var dragTimerId = 0; // 삼성폰 오작동 수정을 위해 추가
$.getScript("https://code.jquery.com/ui/1.12.1/jquery-ui.min.js", function() {
$.getScript("https://cdnjs.cloudflare.com/ajax/libs/jqueryui-touch-punch/0.2.3/jquery.ui.touch-punch.min.js", function() {
floatingToc.draggable({
start: function(event, ui) {
var self = this;
dragTimerId = setTimeout(function(){
$(self).addClass('noclick');
}, 250);
}
});
});
});
// 기존 로직 생략
} else {
// 기존 로직 생략
}
} else {
// 기존 로직 생략
}
}
기존에 이 라이브러리를 사용하고 있지 않은 분이라면 블로그 최초 진입부터 로드를 하게 되면 쓸데 없는 부하가 생기게 되는 것이므로 필요한 시점에 로드하도록 하였습니다.
그런 다음 draggable 메서드를 이용하여 플로팅 목차 엘리먼트에 드래그 기능을 부여하였습니다.
그런데 원래 목차를 클릭하게 되면 접기/펼치기 기능이 수행되므로 드래그 후 그 기능이 작동되는 것을 방지하기 위해 noclick이라는 클래스를 추가하는 트릭을 이용하였습니다.
드래그를 시작하는 시점에 noclick 클래스를 부여하였습니다.
+내용추가 (2020.12.23)
삼성 스마트폰에서 클릭 이벤트가 정상 동작하지 않아서 약간의 트릭을 활용하였습니다.
드래그가 시작되면 바로 noclick 클래스를 추가하지 않고 0.25초 간 유예를 두었습니다.
유예 시간은 너무 짧지도 않고 너무 길지도 않게 적절히 잡으면 될 것 같습니다.
목차 접기/펼치기 클릭 이벤트 수정하기
목차를 드래그 앤 드롭을 하게 되면 마우스를 클릭한 상태로 드래그를 하고 원하는 위치에서 마우스 클릭한 버튼을 떼게 됩니다.
이 때 클릭 이벤트가 발생하는데요.
원래 목차를 클릭하면 접기/펼치기 기능이 수행되었죠.
드래그 앤 드롭 할 때에도 그 기능이 수행되면 안 되므로 수정이 필요합니다.
function appendToc() {
// 기존 로직 생략
if(bookToc.length > 0 && window.scrollY > bookToc.offset().top + bookToc.innerHeight()) {
if(floatingToc.height() === 0) {
// 기존 로직 생략
// 목차 클릭
var clickFlag = true;
function clickTitle() {
if(clickFlag) {
clickFlag = false;
if (floatingToc.hasClass('noclick')) {
floatingToc.removeClass('noclick');
} else {
if(dragTimerId !== 0) {
clearTimeout(dragTimerId);
dragTimerId = 0;
}
floatingToc.css('transition', '');
var title = $('#toc-title>p>span#toggle');
if(title.text() === "펼치기") {
$('#toc-title').css('padding', '10px 0 0 0');
floatingToc.css('padding', '0 10px 0');
floatingToc.css('border-radius', '0');
floatingToc.removeClass('floating-toc-header-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('border-radius', '10px');
floatingToc.addClass('floating-toc-header-ani');
} else {
title.text("접기");
}
});
},200);
}
setTimeout(function(){
clickFlag = true;
}, 500);
}
}
$('#toc-title').on('click', function(){
clickTitle();
});
$('#toc-title').on('touchend', function(){
clickTitle();
});
//목차 항목 클릭
$('#toc-body').find('a').on('click', function(event){
checkPosition();
});
$('#toc-body').find('a').on('touchstart', function(event){
floatingToc.draggable('disable');
});
$('#toc-body').find('a').on('touchend', function(event){
setTimeout(function(){
floatingToc.draggable('enable');
}, 200);
checkPosition();
});
// 기존 로직 생략
} else {
// 기존 로직 생략
}
} else {
// 기존 로직 생략
}
}
클릭 이벤트가 발생했을 때 floatingToc에 noclick 클래스를 가지고 있으면 드래그 중이었다는 의미이므로 원래 기능을 수행하지 않고 noclick 클래스만 제거를 해 주고 드래그 앤 드롭을 하지 않고 클릭만 했다면 기존 기능을 수행해 주도록 한 로직입니다.
터치 이벤트가 끝났을 때도 마찬가지로 noclick 클래스를 제거해 주어야 정상 동작을 합니다.
+내용추가 (2020.12.23)
삼성 스마트폰에서 클릭 이벤트가 정상 동작하지 않아서 약간의 트릭을 활용하였습니다.
드래그 시작 후 0.25초가 지나지 않으면 타이머를 중단시켜 noclick 클래스를 얻지 못하게 하고 클릭 동작을 수행하였습니다.
기종에 따라 클릭 이벤트와 터치 이벤트가 동시에 발생하여 clickFlag를 두어 하나만 실행되도록 하였습니다.
+내용추가 (2020.12.24)
드래그 적용 시 목차 항목을 클릭하면 반응하지 않던 현상을 수정했습니다.
터치 시 드래그와 클릭 이벤트가 간섭을 일으키는 라이브러리 자체 문제인 것으로 보입니다.
따라서 touchstart 이벤트가 발생하면 잠시 드래그를 disable 시킨 뒤 터치가 끝나면 다시 enable 시키는 방법을 사용하였습니다.
전체 소스 제공
이해가 어려우신 분들을 위해 appendToc 함수 전체 소스를 제공하겠습니다.
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) {
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);
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');
var dragTimerId = 0; // 삼성폰 오작동 수정을 위해 추가
$.getScript("https://code.jquery.com/ui/1.12.1/jquery-ui.min.js", function() {
$.getScript("https://cdnjs.cloudflare.com/ajax/libs/jqueryui-touch-punch/0.2.3/jquery.ui.touch-punch.min.js", function() {
floatingToc.draggable({
start: function(event, ui) {
var self = this;
dragTimerId = setTimeout(function(){
$(self).addClass('noclick');
}, 250);
}
});
});
});
floatingToc.css('top', "20px");
floatingToc.css('left', "20px");
floatingToc.css('position', "fixed");
// 목차 클릭
var clickFlag = true;
function clickTitle() {
if(clickFlag) {
clickFlag = false;
if (floatingToc.hasClass('noclick')) {
floatingToc.removeClass('noclick');
} else {
if(dragTimerId !== 0) {
clearTimeout(dragTimerId);
dragTimerId = 0;
}
floatingToc.css('transition', '');
var title = $('#toc-title>p>span#toggle');
if(title.text() === "펼치기") {
$('#toc-title').css('padding', '10px 0 0 0');
floatingToc.css('padding', '0 10px 0');
floatingToc.css('border-radius', '0');
floatingToc.removeClass('floating-toc-header-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('border-radius', '10px');
floatingToc.addClass('floating-toc-header-ani');
} else {
title.text("접기");
}
});
},200);
}
setTimeout(function(){
clickFlag = true;
}, 500);
}
}
$('#toc-title').on('click', function(){
clickTitle();
});
$('#toc-title').on('touchend', function(){
clickTitle();
});
//목차 항목 클릭
$('#toc-body').find('a').on('click', function(event){
checkPosition();
});
$('#toc-body').find('a').on('touchstart', function(event){
floatingToc.draggable('disable');
});
$('#toc-body').find('a').on('touchend', function(event){
setTimeout(function(){
floatingToc.draggable('enable');
}, 200);
checkPosition();
});
floatingToc.css('z-index','-1'); // z-index 값을 -1로 주어 잠시 숨김
clickTitle();
setTimeout(function(){
if($('.entry-content').offset().left < $('.floating-toc').width()+20) {
clickTitle();
} else {
floatingToc.css('z-index',''); // 화면이 충분히 넓다면 바로 다시 보이도록 함
}
}, 500);
setTimeout(function(){
floatingToc.css('z-index',''); // 완전히 접힌 후 다시 보이도록 함
}, 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');
}
}
기존에 적용하신 appendToc 함수 부분만 제거하시고 이 함수를 다시 붙여넣어 주세요.
+내용 추가 (2020.12.23)
Odyssey 스킨을 사용하시는 경우 .entry-content를 .article-view으로 바꿔서 적용해주세요!
Letter 스킨을 사용하시는 경우 .entry-content를 .article_view로 바꿔서 적용해주세요!
결과 확인
처음 플로팅 목차가 생기는 위치는 기존과 동일합니다.
이처럼 위치를 드래그 앤 드롭으로 이동이 가능합니다.
펼치면 우측으로 너무 붙어있어도 화면을 넘어가지는 않습니다.
영상으로도 결과를 보겠습니다.
간단히 적용할 수 있으면서 그 효과는 강력합니다.
이처럼 생각만 하면 이미 만들어져 있는 기능을 가져다 쓸 수 있으므로 코딩 공부를 조금만 하셔도 전혀 모르는 것과는 천지 차이라는걸 알 수 있죠? ㅎㅎ
혹시 조금이라도 코딩 공부에 관심이 생기신 분은 아래 포스팅을 참고해 보세요 :)
감사합니다!
이슈 (2020.12.24) - 조치 및 내용 추가 완료
삼성 스마트폰에서 목차 내 항목 클릭 시 정상적으로 반응하지 않는 문제를 발견하였습니다.
=> 드래그 disable/enable 조정으로 해결했습니다. 자세한 사항은 본문 수정 부분 참고 바랍니다.
'정보 > 블로그 운영팁' 카테고리의 다른 글
티스토리 이미지 확대, 그림자 강조 효과 적용 (10) | 2021.01.16 |
---|---|
이미지 태그 alt 속성 글 수정 없이 입력하는 방법 (고급 버전) (34) | 2020.12.29 |
이미지 태그 alt 속성 글 수정 없이 입력하는 방법 (초급 버전) (48) | 2020.12.27 |
구글 핵심적인 웹 지표 CLS 측정 및 개선 방법 (7) | 2020.12.26 |
티스토리 떠 다니는 플로팅 목차 만드는 방법 (고급 버전) (73) | 2020.12.20 |
구글 애드센스 청구서 주소 확인 PIN 번호 입력하기 (11) | 2020.12.19 |
티스토리 떠 다니는 플로팅 목차 만드는 방법 (초급 버전) (72) | 2020.12.17 |
티스토리 목차 글머리 서식 변경하기 (57) | 2020.12.14 |
댓글