본문 바로가기
HTML,CSS,JS

[javascript] left slide menubar-자바스크립트로 슬라이드 메뉴바 만들기

by 새우하이 2020. 11. 10.

시작에 앞서 해당 코드들에 사용되는 아이콘은 flaticon에서 결제후 다운로드 받은 아이콘임을 알립니다.

필요한 아이콘은 직접 구해서 사용하셔야 합니다.

 

우선 슬라이드 메뉴바를 만들기전에 navigation bar를 만듭니다.

반응형 모바일 웹페이지 구성을 위해 viewport를 설정해주고 

<style>
	@font-face {font-family: 'NEXON Lv2 Gothic Bold'; src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_20-04@2.1/NEXON Lv2 Gothic Bold.woff') format('woff'); font-weight: normal; font-style: normal;}
            
	body{margin:0px;}
	.navBox{
		display:flex;
		justify-content: space-between;
		align-items: center;
		font-weight: bold;
		font-family: 'NEXON Lv2 Gothic Bold';
		font-size:1.5em;
		margin:5px;
	}
	.iconBtn{
		width:35px;
		cursor:pointer;
	}
<style>

각각 요소에 적용될 css를 정의 해준다.

navBox class는 navigation 요소들을 감싸고 정렬해줄 컨테이너로 정의했다.

그리고 내부에는 

뒤로가기 버튼과 햄버거버튼(메뉴버튼)과 title을 위치 시켰다.

<div class="navBox">
	<img class="iconBtn" src="./leftchevron.png" style="margin-left:-5px">
	<span>박지원</span>
	<img class="iconBtn" id="menuBtn" src="./menuBtn.png">
</div>

 

뒤로가기 버튼 style에 margin-left를 준것은 내가 사용하는 아이콘의 비율이 우측 햄버거 버튼아이콘의 비율과 조금 달라 양 옆의 공백을 상쇄시키기 위해 -5px라는 값을 설정 했다. 사용하는 아이콘에 따라 다르게 설정하면 되는 부분이다.

 

<div class="menuWrap">
      <nav id="menu">
        <ul class="menuList">
          <li class="listStyle"><a href="#">Home</a></li>

          <li class="listStyle">
            <a href="#">마이페이지</a>
          </li>
          <li class="listStyle">
            <a href="#">로그인</a>
          </li>
        </ul>
      </nav>
    </div>

navBox 클래스를 가진 div 아래에 슬라이딩메뉴로 사용할 division을 하나 설정해주고 li태그로 리스트들을 설정해준다.

우선

      .menuWrap {
        position: fixed;
        top: 0;
        right: -300px;
        z-index: 400;
        width: 300px;
        height: 100%;
        padding: 50px 20px;
        box-sizing: border-box;
        transition: right 0.3s ease-in-out;
        background-color: #f0f0f0;
      }
      #menuBtn {
        width: 35px;
        z-index: 401;
        transition: all 0.3s ease-in-out;
        cursor: pointer;
      }

메뉴 position을 fixed로 둔 뒤 width size 만큼 right에서 -???px를 사용해서 숨겨준다. transition을 사용해서 애니메이션 속도를 지정해준다.

 

이제 script 부분을 살펴본다.

    <script>
      document.addEventListener("DOMContentLoaded", function () {
        document
          .querySelector("#menuBtn")
          .addEventListener("click", function (e) {
            if (document.querySelector(".menuWrap").classList.contains("on")) {
              //메뉴 slideOut
              document.querySelector(".menuWrap").classList.remove("on");
              //slideOut시 menuBtn의 img src를 menu icon으로 변경
              document.getElementById("menuBtn").src = "./menuBtn.png";
            } else {
              //메뉴 slideIn
              document.querySelector(".menuWrap").classList.add("on");
              //slideIn시 menuBtn의 img src를 cross icon으로 변경
              document.getElementById("menuBtn").src = "./cross.png";
            }
          });
      });
    </script>

addEventLinstener를 사용하여 DOMContentLoaded 가 발생했을때 function을 수행한다. 그리고 버튼아이콘에 click 이벤트가 발생했을 때의 function을 설정하는데 

querySelector가 통해 선택자의 element를 받고 classList 프로퍼티의 contains 메서드로 지정한 클래스 값(.on)이 엘리먼트의 class 속성에 존재하는지 확인한다.

슬라이드 메뉴의 활성여부를 .on 클래스 유무 여부로 판단하는 것이다.

.menuWrap.on {
  right: 0;
}

메뉴를 오른쪽에서 왼쪽으로 slideIn 하기 위해 right:0 으로 지정했다.

 

여기까지 구현이 되었다면 메뉴 아이콘 클릭시 -300px에 의해 숨어있던 메뉴가 나타날 것이다.

X를 눌렀을때 다시 -300px 되며 사라지는 모습도 볼 수 있다.

 

Scroll Lock 구현

이제 slideIn, slideOut은 잘 동작하지만. modal에서 처럼 사용자가 menu에 집중할 수 있도록 배경을 날리고 menu 외에서 발생하는 여러 이벤트들을 막아줘야 한다.

#dimmed {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 300;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
}

우선 dimmed라는 id를 가진 요소를 생성할 것이므로 미리 css 정의를 해주도록 한다. 

rgba를 사용해서 메뉴 밖의 부분은 반투명한 화면으로 만들어 줄것임.

 

slideIn에서 동작하므로

 //메뉴 slideIn
     document.querySelector(".menuWrap").classList.add("on");
     document.getElementById("menuBtn").src = "./cross.png";
     document.querySelector("#menuBtn").classList.add("btnRotate");
     // --- 아래 내용 추가 ---
     let div = document.createElement("div");
     div.id = "dimmed";
     document.body.append(div);

    //페이지 스크롤 락  모바일 이벤트 차단
    document
    	.querySelector("#dimmed")
    	.addEventListener(
    		"scroll touchmove touchend mousewheel",
    		function (e) {
    			e.preventDefault();
    			e.stopPropagation();
    			return false;
    		}
    	);
        .
        .
        .
        생략

기존에 작성한 코드에서 dimmed라는 id를 가진 div element를 생성하여 div라는 변수에 추가해주고 body에 append 시켜준다.

이벤트리스터로 scroll touchmove touchend mousewheel의 동작을 e.preventDefault() 와 e.stopPropagation() 으로 동작을 중지 시켜준다. 이 두개의 이벤트 동작은 이 블로그에 잘 설명되어있어서 링크했다.

 

그리고 slideOut 된 경우에도

 //메뉴 slide
     document.querySelector(".menuWrap").classList.remove("on");
     document.getElementById("menuBtn").src = "./menuBtn.png";
     document.querySelector("#menuBtn").classList.remove("btnRotate");
     // --- 아래 코드를 추가 ---
     document.querySelector("#dimmed").remove();

dimmed를 제거해주면 scroll rock까지 구현된다.

 

CSS 작업

 

      .btnRotate {
        transform: rotate(-90deg);
        transition: all 0.3s ease-in-out;
      }
	생략
    .
 //메뉴 slide
   document.querySelector(".menuWrap").classList.remove("on");
   document.getElementById("menuBtn").src = "./menuBtn.png";
   //추가
   document.querySelector("#menuBtn").classList.remove("btnRotate");
   document.querySelector("#dimmed").remove();
 } else {
 //메뉴 slideIn
   document.querySelector(".menuWrap").classList.add("on");
   document.getElementById("menuBtn").src = "./cross.png";
   //추가
   document.querySelector("#menuBtn").classList.add("btnRotate");
   let div = document.createElement("div");
   .
   .
   .

이렇게 btnRotate를 add remove해주며 구현이 가능하다.

 

완성~

전체코드

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta name="viewport" content="width=device-width, user-scalable=no" />
  </head>
  <body>
    <style>
      @font-face {
        font-family: "NEXON Lv2 Gothic Bold";
        src: url("https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_20-04@2.1/NEXON Lv2 Gothic Bold.woff")
          format("woff");
        font-weight: normal;
        font-style: normal;
      }

      body {
        margin: 0px;
      }
      .navBox {
        display: flex;
        justify-content: space-between;
        align-items: center;
        font-weight: bold;
        font-family: "NEXON Lv2 Gothic Bold";
        font-size: 1.5em;
        margin: 5px;
      }
      .iconBtn {
        width: 35px;
        cursor: pointer;
      }
      .menuWrap.on {
        right: 0;
      }
      .menuWrap {
        position: fixed;
        top: 0;
        right: -300px;
        z-index: 400;
        width: 300px;
        height: 100%;
        padding: 50px 20px;
        box-sizing: border-box;
        transition: right 0.3s ease-in-out;
        background-color: #f0f0f0;
      }
      #menuBtn {
        width: 35px;
        z-index: 401;
        transition: all 0.3s ease-in-out;
        cursor: pointer;
      }
      #dimmed {
        position: fixed;
        top: 0;
        left: 0;
        z-index: 300;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, 0.5);
      }
      .btnRotate {
        transform: rotate(-90deg);
        transition: all 0.3s ease-in-out;
      }
    </style>

    <div class="navBox">
      <img class="iconBtn" src="./leftchevron.png" style="margin-left: -5px" />
      <span>박지원</span>
      <img class="iconBtn" id="menuBtn" src="./menuBtn.png" />
    </div>
    <div class="menuWrap">
      <nav id="menu">
        <ul class="menuList">
          <li class="listStyle"><a href="#">Home</a></li>

          <li class="listStyle">
            <a href="#">마이페이지</a>
          </li>
          <li class="listStyle">
            <a href="#">로그인</a>
          </li>
        </ul>
      </nav>
    </div>
    <script>
      document.addEventListener("DOMContentLoaded", function () {
        document
          .querySelector("#menuBtn")
          .addEventListener("click", function (e) {
            if (document.querySelector(".menuWrap").classList.contains("on")) {
              //메뉴 slide
              document.querySelector(".menuWrap").classList.remove("on");
              document.getElementById("menuBtn").src = "./menuBtn.png";
              document.querySelector("#menuBtn").classList.remove("btnRotate");
              document.querySelector("#dimmed").remove();
            } else {
              //메뉴 slideIn
              document.querySelector(".menuWrap").classList.add("on");
              document.getElementById("menuBtn").src = "./cross.png";
              document.querySelector("#menuBtn").classList.add("btnRotate");
              let div = document.createElement("div");
              div.id = "dimmed";
              document.body.append(div);

              //페이지 스크롤 락  모바일 이벤트 차단
              document
                .querySelector("#dimmed")
                .addEventListener(
                  "scroll touchmove touchend mousewheel",
                  function (e) {
                    e.preventDefault();
                    e.stopPropagation();
                    return false;
                  }
                );
            }
          });
      });
    </script>
  </body>
</html>

 

 

해당 포스트는 apost.kr/702 해당 블로그를 토대로 학습하며 작성했습니다. 

 

슬라이딩 모바일 메뉴 구현

반응형 웹에서 모바일 UI를 구현할 때는 화면 크기의 제약으로 인해 화면 위나 옆에서 메뉴가 펼쳐지면서 보이도록 구현합니다. 좁은 모바일 화면을 최대한 활용하는 방법이기 때문에 주로 이

apost.kr

 

'HTML,CSS,JS' 카테고리의 다른 글

[CSS] 심플한 로그인폼 구현  (1) 2021.04.24
CSS 마진(여백) 상쇄  (0) 2020.08.12
홈페이지에 github(깃허브) profile card 삽입하기  (0) 2019.11.15
클립보드 모양 CSS  (0) 2019.10.03
css 가로 세로 중앙 정렬  (0) 2019.09.01

댓글