아래 이미지와 같이 누구나 한 번쯤은 '현재 페이지'에서 원하는 내용을 찾기 위해 '컨트롤(Ctrl) + F'로 단어 검색을 해 본 적이 있을 것이다.
먼저 이 '컨트롤(Ctrl) +F 단어 검색 기능'을 요약정리해 보겠다. (이 부분을 잘 알아야 뒤에서 나오는 HTML 및 CSS를 더 잘 이해할 수 있다.)
① '현재 페이지'에서 '검색 단어'와 일치하는 단어 (이하 '일치 단어') 찾기
② '일치 단어'의 전체 개수를 숫자로 표기 (ex : 36)
③ '일치 단어'를 노란색으로 마크(mark) 처리
④ 상단에서 하단 방향으로 '일치 단어'에 자동으로 번호 지정
⑤ '일치 단어' 중 '현재 단어'의 번호를 숫자로 표기 (ex : 10)
⑥ '일치 단어' 중 '현재 단어'를 주황색으로 마크(mark) 처리
⑦ '∧' 버튼 클릭 시 '이전 단어' 찾기
- 화면 이동, 마크(mark) 색상 변경, 번호 변경 (-1)
⑧ '∨' 버튼 클릭 시 '다음 단어' 찾기
- 화면 이동, 마크(mark) 색상 변경, (+1)
⑨ 'Ⅹ' 버튼 클릭 시 검색 결과 초기화 및 검색창 제거
상기 요약 내용과 같이 웹사이트 방문자가 원하는 내용을 쉽고 빠르게 찾을 수 있도록 도와주는 '현재 페이지 내 단어 검색 기능'은 웹사이트 이용 환경 구축에 필수 요소라고 할 수 있다.
이미 PC에는 '컨트롤(Ctrl) + F 단어 검색 기능'이 있고 Mobile에는 '페이지 내 검색 기능'이 있다. 그런데 이 기능을 알고 있는 방문자의 경우 웹사이트를 잘 이용할 수 있지만, 이 기능의 존재 유무조차 모르는 방문자의 경우 웹사이트에 오래 머물지 못하고 쉽게 이탈할 수도 있다.
'수익형 티스토리 블로그'를 생각해 볼 때 방문자의 만족도는 물론, 체류 시간 증대와 이탈률 감소를 위해 화면 상단에 고정된 플로팅 검색창을 적용하는 것이 좋겠다.
본 글은 북클럽 스킨을 기준으로 현재 페이지에서 찾아 주는 플로팅 검색창을 생성하는 방법에 대한 내용이며, 상세히 설명했으니 각자의 티스토리에 적용해 보기 바란다.
미리 보기
먼저 작업 결과이다.
상기 설명한 '컨트롤(ctrl) + F 단어 검색 기능'과 거의 동일하게 구현했고, Mobile에서도 이상 없이 작동하는 것을 확인할 수 있다.
PC든 Mobile이든 글이 길면 이탈률이 높아질 것 같아서 걱정이 되면서도 체념할 수밖에 없었는데, 플로팅 검색창을 달고 나니 마음이 한결 가벼워졌다.
1] 검색창 생성
가장 먼저 검색창을 생성한다.
검색창은 웹사이트 방문자가 '현재 페이지'의 첫 화면에서 스크롤을 내리기 전에 쉽게 발견할 수 있어야 하기 때문에, 기본적으로 검색창의 위치는 '현재 페이지'의 최상단이 가장 좋다.
검색창을 본문 최상단에 생성하기 위해 아래 코드를 HTML의 <div class="entry-content"> 위에 삽입한다.
<div class="search-in-body">
<input type="search" class="search-box" placeholder="본문 검색">
<button data-search="next" class="next-btn">∨</button>
<button data-search="prev" class="prev-btn">∧</button>
<button data-search="clear" class="clear-btn">Ⅹ</button>
<span class="kwt-count">-</span>
</div>
<div class="entry-content">
##_article_rep_desc_##
[ 2 ]
input type을 search로 해야 '단어 입력란'이 생성된다.
placeholder는 '단어 입력란'에 단어를 입력하기 전, '단어 입력란'에 고정적으로 출력되어 있는 문구에 해당하며, 나는 웹사이트 방문자의 이해를 돕고 검색을 유도하기 위해 '본문 검색'을 삽입했다.
[ 3 ~ 5 ]
button data-search로 '다음 단어', '이전 단어', '단어 입력란 초기화' 버튼을 생성한다. 버튼 안에 들어가는 내용은 <button ~ > ~ </button> 사이에 삽입하면 되는데, 나는 검색창의 면적을 최소화하기 위해 기호('∧', '∨', 'ⅹ')를 삽입했다.
[ 6 ]
kwt-count는 '숫자('현재 단어'의 번호) / 숫자('일치 단어'의 전체 개수)'에 해당한다. <span ~ > ~ </span> 사이에는 단어를 검색하기 전에 출력할 문구를 삽입하면 되는데, 나는 '하이픈(-)'을 삽입했다. ('0 / 0' 도 가능)
[ 1 ]
마지막으로 CSS 스타일 적용을 위해 '검색창 전체'를 div 코드로 감싸 주었다. search-in-body 포함 모든 'class'의 값들은 수정 가능하다.
상기 코드를 삽입하면 검색창이 아래와 같이 생성된다.
아래 코드를 CSS에 삽입해서 레이아웃 등 검색창의 기본적인 스타일을 적용한다.
.search-in-body {
display: flex;
flex-direction: row;
background-color: #eee;
z-index: 999;
}
.search-box {
width: 40%;
border: 1px solid #eee;
padding: 3px 3px 3px 3px;
margin: 5px 3px 5px 5px;
}
.next-btn,
.prev-btn,
.clear-btn {
width: 10%;
padding: 0px 3px 0px 3px;
margin: 5px 3px 5px 3px;
border: 2px solid white;
}
.kwt-count {
display: flex;
justify-content: center;
align-items: center;
width: 30%;
padding: 0px 3px 0px 3px;
margin: 5px 5px 5px 3px;
border: 2px solid white;
}
[ 1 ~ 6 / search-in-body ]
'검색창 전체'에 해당하는 코드다.
display: flex;, flex-direction: row;로 가로 정렬을 하고, 검색창과 본문의 영역을 구분 지을 수 있도록 background-color로 검색창의 배경 색상을 지정하고, 검색창이 다른 개체에 가려지지 않도록 z-index로 검색창의 Z축을 지정한다.
[ 8, 16, 25 / width ]
width는 가로길이에 해당하며, 출력될 위치에 따라 자동으로 가로길이가 정해지도록 가로길이를 'px' 단위가 아닌 '%' 단위로 지정했다. 해서 search-box는 '40%;', btn은 각각 '10%;', kwt-count는 '30%;'의 비율을 지정하여 '100%'를 맞췄다. 내가 search-in-body의 width를 별도로 지정하지 않았기 때문에, 본문 영역의 가로길이에 따라 자동으로 가로길이가 정해진다.
[ border, padding, margin ]
각각의 영역을 구분해주고, 간격을 맞추기 위해 적정 값을 지정한다.
[ 22 ~ 24 ]
kwt-count는 버튼 안에 들어가는 것이 아니기 때문에 기본적으로 중앙 정렬이 되어 있지 않다. display: flex;, justify-content: center;, align-items: center;로 중앙 정렬 한다.
상기 코드를 삽입하면 검색창이 아래와 같이 기본적인 외형을 갖춘다.
2] var, 변수 선언
아래 var 코드 [ 3 ~ 20 ] 를 HTML에 추가 삽입해서 변수를 선언한다.
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mark.js/8.11.1/mark.min.js"></script>
<script>
document.body.onload = function() {
var $input = document.querySelector("input[type='search']"),
$clearBtn = document.querySelector("button[data-search='clear']"),
$prevBtn = document.querySelector("button[data-search='prev']"),
$nextBtn = document.querySelector("button[data-search='next']"),
$content = document.querySelector(".entry-content"),
$contentMark = new Mark($content),
$results,
currentClass = "current",
offsetTop = 200,
currentIndex = 0;
var variableCounter = 0;
var totalCount = 0;
};
</script>
<div class="entry-content">
##_article_rep_desc_##
[ 3 / mark.min.js ]
mark 자바스크립트 코드를 삽입해야 검색 시 '일치 단어'에 노란색 마크(mark) 처리가 정상적으로 작동한다.
[ 11 / content ]
북클럽 스킨을 기준으로 본문에 해당하는 entry-content를 삽입한다.
[ 15 / offsetTop ]
'현재 단어'가 화면 상 위치하는 높이에 해당하며, 검색에 따른 화면 이동 시 이 값을 기준으로 이동한다.
[ 16 / currentIndex ]
현재 활성화된 인덱스에 해당하며, '0'으로 지정한다.
[ 17 / variableCounter ]
'현재 단어'의 번호에 해당하며, '0'으로 지정한다.
[ 18 / totalCount ]
'일치 단어'의 전체 개수에 해당하며, '0'으로 지정한다.
3] input, 기능 설정
아래 input 코드 [ 4 ~ 12 ] 를 HTML에 추가 삽입해서 'input' 기능을 설정한다.
'단어 입력란'에 단어('value')를 입력하면 '일치 단어'를 마크(mark) 처리한다는 내용이다.
<script>
var totalCount = 0;
$input.addEventListener("input", function() {
var searchVal = this.value;
$contentMark.unmark( {
done: function(totalMatches) {
$contentMark.mark(searchVal, {
});
}
});
});
};
</script>
<div class="entry-content">
##_article_rep_desc_##
아래 kwt-count 코드 [ 4 ~ 13 ] 를 HTML에 추가 삽입해서 '일치 단어'의 전체 개수에 대한 'count' 기능을 설정한다.
'일치 단어'의 전체 개수를 totalCount로 설정하고 '현재 단어'의 번호를 '1'로 지정해서, kwt-count를 '숫자('현재 단어'의 번호) / 숫자('일치 단어'의 전체 개수)'로 표기한다는 내용이다.
<script>
$contentMark.mark(searchVal, {
separateWordSearch: false,
acrossElements: true,
done: function(totalMatches) {
$results = $content.querySelectorAll("mark");
totalCount = totalMatches;
if (totalCount) {
variableCounter = 1;
$(".kwt-count").html(variableCounter+ " / " +totalCount);
}
}
});
}
});
});
};
</script>
<div class="entry-content">
##_article_rep_desc_##
아래 mark 코드를 CSS에 추가 삽입해서 '일치 단어'의 마크(mark) 처리를 노란색으로, '현재 단어'의 마크(mark) 처리를 주황색으로 설정한다.
mark {
background: yellow;
}
mark.current {
background: orange;
}
여기까지 완료하면 검색창이 아래와 같이 나타난다.
4] prev & nextBtn, 찾기 기능 설정
아래 prevBtn, nextBtn 코드 [ 10 ~ 24 ] 를 HTML에 추가 삽입해서 '이전 및 다음 버튼'의 찾기 기능을 설정한다.
<script>
$(".kwt-count").html(variableCounter+ " / " +totalCount);
}
}
});
}
});
});
$nextBtn.addEventListener("click", prevNextHandler);
$prevBtn.addEventListener("click", prevNextHandler);
$nextBtn.after($prevBtn);
function prevNextHandler() {
if ($results.length) {
currentIndex += (this.dataset.search === "prev" ? -1 : 1);
if (currentIndex < 0) {
currentIndex = $results.length - 1;
}
if (currentIndex > $results.length - 1) {
currentIndex = 0;
}
jumpTo();
}
}
};
</script>
<div class="entry-content">
##_article_rep_desc_##
[ 10, 11 ]
버튼 클릭에 대한 이벤트 코드로, 이 코드를 삽입해야 버튼 클릭 기능이 정상적으로 작동한다.
[ 15 ]
'-1'과 '1'로 삽입해야 버튼을 클릭했을 때 '현재 단어'에서 '이전 및 다음 단어'를 한 단계씩 찾는다. 예를 들어 '-2'와 '2'로 삽입할 경우 '10번 단어'에서 '다음 버튼'을 클릭 시 '12번 단어'를 찾게 되고, '10번 단어'에서 '이전 버튼'을 클릭 시 '8번 단어'를 찾게 된다.
[ 16 ~ 21 ]
이 코드를 삽입해야 '1번 단어'에서 '이전 버튼'을 클릭 시 '마지막 단어'를 찾을 수 있고, '마지막 단어'에서 '다음 버튼'을 클릭 시 '1번 단어'를 찾을 수 있다.
5] prev & nextBtn, Count 기능 설정
아래 count 코드 [ 6 ~ 20 ] 를 HTML 추가 삽입해서 '현재 단어'의 번호에 대한 'count' 기능을 설정한다.
'이전 버튼'을 클릭 시 '현재 단어'의 번호에서 '-1' 처리되고, '다음 버튼'을 클릭 시 '현재 단어'의 번호에서 '+1' 처리된다는 내용이다.
<script>
jumpTo();
}
}
$("[data-search=next]").click(function() {
if (variableCounter < totalCount)
variableCounter = variableCounter + 1;
else
variableCounter = 1;
$(".kwt-count").html(variableCounter+ " / " +totalCount);
})
$("[data-search=prev]").click(function() {
if (variableCounter > 1)
variableCounter = variableCounter - 1;
else
variableCounter = totalCount;
$(".kwt-count").html(variableCounter+ " / " +totalCount);
})
};
</script>
<div class="entry-content">
##_article_rep_desc_##
6] jumpTo, 화면 이동 기능 설정
아래 jumpTo 코드 [ 5 ~ 16 ] 를 HTML에 추가 삽입해서 버튼 클릭 시 화면 이동 기능을 설정한다.
앞서 '현재 단어'의 offsetTop을 설정했는데, 버튼 클릭 시 설정한 offsetTop 위치에 맞게 화면을 이동시킨다는 내용이다.
<script>
$(".kwt-count").html(variableCounter+ " / " +totalCount);
})
function jumpTo() {
if ($results.length) {
var position,
$current = $results[currentIndex];
$results.forEach($result => $result.classList.remove(currentClass));
if ($current) {
$current.classList.add(currentClass);
position = $current.offsetTop - offsetTop;
window.scrollTo(0, position);
}
}
}
};
</script>
<div class="entry-content">
##_article_rep_desc_##
7] clearBtn, 초기화 기능 설정
아래 clearBtn 코드 [ 7 ~ 14 ] 를 HTML에 추가 삽입해서 '클리어 버튼'의 초기화 기능을 설정한다.
버튼 클릭 시 '일치 단어'가 언마크(unmark) 처리되고, '단어 입력란'에 입력된 단어가 삭제되고, kwt-count의 문구가 '하이픈(-)'으로 변경된다는 내용이다.
<script>
window.scrollTo(0, position);
}
}
}
$clearBtn.addEventListener("click", function() {
$contentMark.unmark();
$input.value = ""
$input.focus();
variableCounter = 0;
totalCount = 0;
$(".kwt-count").html("-");
});
};
</script>
<div class="entry-content">
##_article_rep_desc_##
8] 화면 우측 고정 설정
아래 코드를 CSS에 추가 삽입해서 검색창을 화면 우측에 고정한다.
position: fixed;로 검색창의 위치를 고정시키고, 고정시킬 위치는 right: 20px;로 지정한다. 또한 우측 공간의 면적을 고려해서 검색창의 가로길이를 width: 300px;로 지정한다.
.search-in-body {
position: fixed;
right: 20px;
width: 300px;
box-shadow: 0px 0px 5px rgba(0,0,0,0.5);
}
상기 코드를 삽입하면 검색창이 아래와 같이 화면 우측에 고정된다.
9] 화면 우측 상단 플로팅 설정
아래 scroll 코드 [ 7 ~ 21 ]를 HTML에 추가 삽입해서 검색창을 화면 우측 상단에 플로팅으로 고정한다.
스크롤을 일정 부분까지 내리면 CSS 스타일 중 sib-fixed가 적용되고 sib-absolute는 해제된다는 내용이고, 스크롤을 일정 부분까지 올리면 반대로 sib-absolute가 적용되고 sib-fixed는 해제된다는 내용이다.
<script>
$(".kwt-count").html("-");
});
};
$(document).ready(function () {
$(".search-in-body").addClass("sib-absolute");
var sib = $(".search-in-body").offset().top - 165;
$(window).scroll(function () {
if ($(this).scrollTop() >= sib) {
$(".search-in-body").addClass("sib-fixed");
$(".search-in-body").removeClass("sib-absolute");
}
else {
$(".search-in-body").addClass("sib-absolute");
$(".search-in-body").removeClass("sib-fixed");
}
});
});
</script>
<div class="entry-content">
##_article_rep_desc_##
아래 코드를 CSS에 추가 삽입하고 search-in-body의 position: fixed;는 삭제한다.
.sib-absolute {
position: absolute;
}
.sib-fixed {
position: fixed;
top: 80px;
}
여기까지 완료하면 검색창이 아래와 같이 생성된다.
10] Mobile에서의 스타일 적용
아래 코드를 CSS에 추가 삽입해서 Mobile 화면에서의 스타일을 적용한다. (세부 내용 생략)
@media screen and (max-width: 800px) {
.search-in-body {
display: flex;
flex-direction: row;
position: fixed;
right: 20px;
left: 20px;
width: auto;
top: 75px;
}
.search-box {
width: 40%;
margin-left: 5px;
padding-left: 10px;
}
.next-btn,
.prev-btn {
width: 10%;
padding-right: 3px;
padding-left: 3px;
margin-right: 3px;
margin-left: 3px;
color: black;
}
.clear-btn {
width: 10%;
padding-right: 3px;
padding-left: 3px;
margin-right: 3px;
margin-left: 3px;
color: black;
}
.kwt-count {
width: 30%;
display: flex;
justify-content: center;
align-items: center;
padding-right: 3px;
padding-left: 3px;
margin-right: 5px;
margin-left: 3px;
color: black;
}
}
11] HTML 전체 코드
12] CSS 전체 코드