내 블로그의 Topbar와 같이 화면을 스크롤 해도 항상 그 자리에 고정돼 있는 Topbar를 구현하자. 내 블로그의 Fixed Topbar는 Fixed Topbar 구현 포스트의 방법2 를 바탕으로 제작되었다. 이 포스트에서는 같은 방법을 Minimal-Mistakes 테마에 적용하는 과정을 포스팅 할 것이다.

과정

과정을 진행하면서 Topbar를 페이지 최상위에 고정 시키고 Fixed Topbar 구현 포스트에서 제기했던 HTML 요소가 가려지는 문제를 해결할 것이다.

1. Topbar 고정

mmistakes 테마에서 Topbar를 페이지 최상위에 고정시키기위해 아래와 같은 CSS 를 적용한다.

1
2
3
4
5
6
.masthead {
  position: fixed;
  top: 0;
  height: calc(3 * 16px);
  width: 100%;
}

코드의 4번째 줄에서 Topbar의 높이 calc(3 * 16px);는 48px가 된다.

2. 내용 여백 조정

Fixed Topbar로 인해 포스트 내용이 가려지는 것을 방지하기 위해 내용이 약간 아래부터 시작하도록 윗쪽 여백을 넣는다.

1
2
3
body {
  padding-top: calc(3 * 16px + 8px);
}

Fixed Topbar의 높이(3*16px=48px)에 +8px 만큼의 여백을 더 추가하였다.

3. Javascript 작성

위 두가지 css 설정으로 해결 되지 않는 문제들을 javascript를 이용해서 해결한다.
Fixed Topbar 구현 포스트에서 제기한 문제 상황 2, 3이 여기에 해당한다.

스크롤 목적지를 살짝 위로 설정

먼저 페이지를 스크롤하는 함수를 정의한다.

1
2
3
4
5
6
top_offset = 99;
function scrollToHash(h) {
  // 스크롤 위치를 목적지의 y 좌표 보다 top_offset 만큼 위의 좌표로 계산
  var yPos = $(h).offset().top - top_offset;
  scrollTo({top: yPos, behavior: 'smooth'});
}

자동 스크롤을 하면 기본적으로 브라우저 화면 맨 꼭대기에 스크롤 목적지의 y좌표가 위치하도록 스크롤 된다. 이때 스크롤 목적지의 y좌표에 top_offset 만큼을 빼주면 브라우저 화면 맨 꼭대기에 top_offset 픽셀 만큼 위의 y좌표가 오게된다.
즉, 실제 목적지는 브라우저 화면 꼭대기에서 부터 top_offset 만큼 아래에 있도록 스크롤 된다.
또한 scrollTo()(스크롤 함수)에 behavior: 'smooth' 옵션을 넣어서 부드러운 스크롤링을 설정했다.

Windows에서 애니메이션 표시

참고로 Windows OS에서 부드러운 스크롤링은 Windows에서 애니메이션 표시 옵션을 켜야 정상적으로 동작 한다.

다른 페이지로 부터 이동해 온 다음 특정 위치로 자동 스크롤

다른 페이지에서 링크를 클릭하면 링크의 href 경로로 페이지 이동한다. 이때 hrefhash1가 있으면 브라우저는 페이지 이동한 후 해당 hash의 위치로 화면을 자동 스크롤 한다.

1
2
3
4
5
6
$(function() {
  if(location.hash.length > 0) {
    var h = decodeURIComponent(location.hash);
    scrollToHash(h);
  }
});

$(function() { }); 로 시작하는 함수는 jquery에서 지원하는 $( document ).ready(function() { }); 함수의 축약형이다. 이 함수는 페이지의 로드가 완료되면 실행 된다는 특징이 있다.
이 함수를 이용해서 위와 같이 코드를 짜면 페이지 로드가 완료되었을 때 현재 URL에 hash가 포함되어 있는지 확인하고 hash가 포함돼 있다면 해당 hash의 y 좌표로 스크롤 한다.

같은 페이지 내에서 다른 위치로 자동 스크롤

링크를 클릭하면 링크의 href 경로로 이동하는데 그 경로가 현재 페이지의 주소와 같은 경우 페이지의 이동 없이 href에 있는 hash의 위치로 자동 스크롤 한다.

1
2
3
4
5
6
7
8
9
$('a').click(function (e) {
  if($(this)[0].origin == location.origin &&
     $(this)[0].pathname == location.pathname) {
    e.preventDefault();
    var h = $(this).attr('href');
    scrollToHash(h);
    history.pushState({}, null, h);
  }
});

링크의 href를 이용하므로 <a> 태그에서만 동작한다. 다른 태그에서도 같은 스크롤 기능을 구현 하려면 $('a') 부분을 변경해 주면 된다.
두번째 줄에 있는 조건문의 origin은 URL의 root 주소를 의미한다. 따라서 이동하려는 목적지와 현재 페이지의 root 주소가 같은지 확인 할 수 있다. 내 블로그의 root 주소는 https://seungwubaek.github.io가 된다.
이어서 세번째 줄의 pathname은 root 주소 이후로 이어지는 주소를 의미한다. 여기까지는 hash가 포함되지 않는다.
즉, 조건문의 참이 되는 주소는 이동하려는 목적 주소가 현재 페이지와 같은 주소이다. e.preventDefault();를 이용해 기본으로 설정되어 있는 자동 스크롤 기능을 해제한다.
그리고 링크가 가진 hash 값을 인자로 하는 스크롤 함수(scrollToHash)를 실행시킨다.
마지막으로 history.pushState 함수로 href가 가진 hash 값을 이동한 곳에 맞게 변경해준다.

history

history라는 객체는 페이지 이동 히스토리를 저장하고 있다. 브라우저의 뒤로/앞으로 가기 기능은 이 객체가 있기 때문에 가능하다. 그래서 위 코드와 같이 history.pushState 를 해주면 historyhash를 포함하는 href가 저장되고 뒤로/앞으로 가기 기능을 그대로 사용할 수 있다.

Javascript 전체 코드

위 코드 조각들을 한번에 쓰면 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$(function() {
  top_offset = 99;
  function scrollToHash(h) {
    var yPos = $(h).offset().top - top_offset;
    scrollTo({top: yPos, behavior: 'smooth'});
  }
  // 외부 페이지로부터 이동해 온 후 다른 위치로 스크롤 할 때
  if(location.hash.length > 0) {
    var h = decodeURIComponent(location.hash);
      scrollToHash(h);
  }
  // 같은 페이지에서 다른 위치로 스크롤 할 때
  $('a').click(function (e) {
    if($(this)[0].origin == location.origin &&
       $(this)[0].pathname == location.pathname) {
      e.preventDefault();
      var h = $(this).attr('href');
      scrollToHash(h);
      history.pushState({}, null, h);
    }
  });
});
  1. hash: href 끝에 #으로 시작하는 문자열. 요소의 id를 의미. 

Meta Info

Categories:

Published At:

Modified At:

Leave a comment