ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Sticky Nav 🩹
    Web/JS30 2020. 12. 23. 19:12
    반응형

    예전에 jQuery를 많이 쓰던 때에 유행하던 스타일의 네비게이션 바로 기억한다.

    네비게이션 바가 존재하고, 스크롤을 내리면서 네비게이션 바의 상단이 브라우저 상단에 부딪히는 순간

    상단에 위치가 고정된 채로 스크롤과 함께 움직이는 그런 네비게이션 바.

    오늘은 그것을 라이브러리 없이 만들어보는 예제이다.

     

    일단은 네비게이션 바를 변수에 담아준다.

    그리고, 네비게이션바의 상단이 전체 페이지(문서)의 최상단에서

    얼마만큼 떨어져있는지를 의미하는 변수인 topOfNav를 만들어준다.

    const nav = document.querySelector('#main');
    const topOfNav = nav.offsetTop;

    언제 네비게이션 바의 상단이 화면의 상단에 부딪히는지

    스크롤이 될때마다 항상 지켜보고 있어야 하기 때문에 scroll 이벤트에 리스너를 달아준다.

    function fixNav() {
      console.log(topOfNav, window.scrollY);
    }
    
    window.addEventListener('scroll', fixNav);

    위와 같이 콘솔에 로그를 찍어보면 topOfNavwindow.scrollY가 의미하는 것을 한 번에 파악할 수 있을 것이다.

    네비게이션 바의 상단은 페이지의 최상단에서 485px만큼 떨어져있고,

    window.scrollY는 내가 스크롤을 할 때마다 얼마만큼 스크롤이 되었는지px 단위로 알려준다.

    (현재 화면의 최상단이, 전체 페이지의 최상단으로부터 얼마만큼 아래로 움직였는지)

     

    우리의 목적은, 네비게이션 바가 현재 화면의 최상단에 부딪히는 순간

    네비게이션 바를 스크롤과 함께 움직이도록 하는 것이므로

    window.scrollY가 topOfNav보다 커지는 순간을 if문으로 포착하면 된다.

    function fixNav() {
        if (window.scrollY >= topOfNav) {
          document.body.classList.add('fixed-nav');
        } else {
          document.body.classList.remove('fixed-nav');
        }
      }
    
      window.addEventListener('scroll', fixNav);

    window.scrollY가 topOfNav보다 커지는 순간 문서의 body에다가 'fixed-nav'라는 클래스를 붙여준다.

    (반대로, 다시 작아지면 fixed-nav를 없애준다)

     

    이제 fixed-nav 클래스에 CSS를 설정해줄 차례이다.

    .fixed-nav nav {
      position: fixed;
      box-shadow: 0 5px rgba(0, 0, 0, 0.1);
    }

    간단하다.

    body에 fixed-nav 클래스가 붙으면 네비게이션 바(nav)의 position을 fixed로 설정해주면 된다.

    그림자는 그냥 네비게이션 바 아래의 내용과 구별이 잘 되게끔 하기 위한 약간의 효과.


    근데, 여기까지만 하면 약간의 문제가 눈을 거슬리게 한다.

    잘 보면, 스크롤을 해서 네비게이션 바가 고정되면 아래에 있는 다른 내용들이

    갑자기 위로 일정 길이만큼 튀어오르는 현상을 볼 수 있을 것이다.

     

    문제는 바로 'fixed' position으로 인해 생기는 것이다.

    특정 요소가 fixed가 되면, 그 요소는 더 이상 해당 document에서 아무런 공간을 차지하지 않는다.

    다른 요소들은 다 땅에서 움직이는데, fixed는 비행기처럼 붕 떠있는 상태라고 보면 된다.

    그래서 해당 요소가 차지하던 공간이 사라지게 되면서, 다른 요소들이 그 공간만큼 위로 당겨지게 되는 것.

     

    이 문제를 해결하기 위해서는

    fixed-nav 클래스를 붙여줌과 동시에 문서의 body 상단에 padding을 끼워넣어주면 된다.

    (반대로, 다시 위로 스크롤을 해서 네비게이션 바의 fixed가 풀리면, padding도 다시 없애주면 된다)

    function fixNav() {
        if (window.scrollY >= topOfNav) {
          document.body.style.paddingTop = nav.offsetHeight + 'px';
          document.body.classList.add('fixed-nav');
        } else {
          document.body.style.paddingTop = 0;
          document.body.classList.remove('fixed-nav');
        }
      }

    fixed-nav 클래스가 붙음과 동시에 페이지의 상단부에 nav.offsetHeight,

    네비게이션 바의 높이만큼 padding-top을 더해준 것을 알 수 있다.

     

    참고로, 여기서 "padding-top도 그냥 .fixed-nav의 CSS로 붙여주면 안되나요?" 라는 의문이 생길 수도 있을 것인데

    이는 '숫자를 하드코딩하지 말고, 상수로 선언해서 사용해라' 라는 프로그래밍 팁과 일맥상통하는 부분이다.

    CSS에서 padding-top을 추가하려면 숫자를 하드코딩해서 사용해야 하는데,

    네비게이션 바의 높이는 우리가 언제 어떤 필요로 인해 어떻게 변경할 지 아무도 모르는 일이다.

     

    그러한 변화에 대응하기 위해서는 nav.offsetHeight 라는 변수를 사용해주는 것이 바람직하다.

    이를 위해서 CSS를 직접 이용하는 게 아니라, 자바스크립트로 CSS를 제어해서 padding을 붙여주는 것.

     

    이를 두고, "'하드코딩' 하지 않고 '프로그래밍적'으로 문제를 해결한다" 라고 많이들 하더라.

     

    여튼, 이렇게 padding을 처리해주면 아래와 같은 자연스러운 스크롤이 가능해진다.


    추가로, 간혹 가다가 보면 네비게이션 바가 fixed 됨과 동시에

    네비게이션 바의 좌측이나 우측에서 로고가 튀어나오는 효과를 주는 경우도 볼 수 있다.

     

    이런 것들은 대부분 로고를 미리 만들어놓고, overflow를 hidden으로 해두고

    max-width를 0으로 해 둔 다음, 스크롤이 되면 max-width를 늘리는 식으로 구현하게 되는데

    이 때 width가 아니라 max-width를 이용해야 한다는 것을 알아두길 바란다.

    li.logo {
      max-width: 0;
      overflow: hidden;
      background: white;
      transition: all .5s;
      font-weight: 600;
      font-size: 30px;
    }
    
    .fixed-nav li.logo {
      max-width: 500px;
    }

    여기서, width를 0에서 auto로 늘리면 로고 크기만큼 자연스럽게 코딩하는 게 가능할텐데,

    왜 굳이 위에서 말한 것과 다르게 하드코딩을 해서 max-width를 사용하는가 하고 생각할 수 있다.

     

    하지만 width를 0에서 auto로 변화시키는 애니메이션은 불가능하다.

    왜 그런지 자세히는 모르겠지만(auto가 동적인 성격을 띠고 있어서 그런 게 아닐까) 여튼 동작이 안된다.

    그래서, max-width를 로고의 크기보다 넉넉하게 주면

    로고가 자연스럽게 띠요옹 하고 튀어나오는 효과를 줄 수 있다.

     

    max-width를 이용하는 것 말고도 다른 해결책이 존재하는데, 이는 참고로 붙여준 블로그에서 확인하면 된다.

     

    참고

     

    [CSS 고급 애니메이션] 텍스트 길이에 따라 자동으로 늘어나는 띠 배너 만들기

    위처럼 새로운 기능이 추가됨을 알리는 띠배너 애니메이션을 구현한 경험을 정리해보고자 합니다. 텍스트 길이에 따라서 자동으로 너비가 지정되게끔 하고 싶었는데 문제는 css의 keyframes로 width

    simsimjae.medium.com

     

    반응형

    'Web > JS30' 카테고리의 다른 글

    Stripe Follow Along Nav  (0) 2020.12.26
    Event Capture, Propagation, Bubbling and Once  (0) 2020.12.24
    📢 Speech Synthesis 🔊  (0) 2020.12.23
    Following Along Links  (0) 2020.11.29
    🎤Native Speech Recognition📢  (0) 2020.11.23

    댓글

Designed by Tistory.