본문 바로가기
FE 개발/JavaScript

[반응형 UI] PC Mobile 한번에 사용가능 한 제이쿼리(jQuery) 햄버거 GNB 메뉴

by Daniel Roh 2024. 1. 4.
반응형

 

반응형 웹에서 사용하면 좋을
제이쿼리(jQuery) GNB

 

들어가기에 앞서

반응형 웹사이트를 구축하기위해서 기존에 사용하던 PC GNB(Global Navigation Bar)와 모바일 햄버거 메뉴(mobile burger menu)를 각각 만들어 CSS show/hide로 기능을 구현했었습니다. 그러던 중 지난 프로젝트에서 사용했던 유용한 코드가 있어서 이웃님들께 소개해 드려봅니다.

 

아래 코드는 HTML5, CSS3, jQuery를 원할하게 이해하고 수정할 수 있는 능력이 필요합니다. 난이도 (중급 / ★★☆ ☆) 제이쿼리 버전은 1.8.21.이며, 최신버전에서도 정상적으로 동작합니다.

 

HTML5

<!-- [S] header -->
<header class="header">
  <div class="header-inner">
    <h1 class="logo"><a href="/"><img src="/images/resp/logo.png" alt="logo" /></a></h1>
  </div>
  <!-- [S] mobile -->
  <div class="mo-gnb-bg"></div>
  <span class="mo-gnb-open">
    <button type="button"><span class="skip">메뉴열기</span></button>
  </span>
  <!-- [S] gnb-box -->
  <div class="gnb-box">
    <div class="gnb-box-inner">
      <span class="mo-gnb-close">
        <button type="button"><span class="skip">메뉴닫기</span></button>
      </span>
      <!-- [S] gnb -->
      <div class="gnb" id="gnb">
        <h2 class="skip">상단 메인메뉴 영역</h2>
        <ul>
          <!-- [S] menu1 -->
          <li class="depth1 menu1">
            <a href="#" class="tit">대메뉴</a><!-- on : 서브페이지 메뉴 활성화 -->
            <!-- [S] depth2 -->
            <div class="top2m menuBox">
              <div class="top2m-inner">
                <ul class="top2m-list">
                  <li class="depth2 depth2-m1">
                    <a class="depth2-tit plus" href="#"><span class="depth2_text">depth2</span></a>
                    <div class="top3m">
                      <ul class="MkSub">
                        <li><a href="#">depth3</a></li>
                        <li><a href="#">depth3</a></li>
                        <li><a href="#">depth3</a></li>
                      </ul>
                    </div>
                  </li>
                </ul>
              </div>
            </div>
            <!--// [E] depth2 -->
          </li>
        </ul>
      </div>
      <!-- <div class="pc-gnb-bg"></div> -->
    </div>
  </div>
  <!-- // [E] gnb-box -->
</header>
<!-- // [E] header -->

 

CSS3

반응형 웹 코드를 작성하기 때문에 CSS에는 미디어 쿼리를 사용하여 디바이스 분기를 해주어야 합니다.

  • 디바이스 분기
    • laptop ~ PC(1024이상)
    • 타블렛 (769~1023)
    • mobile (320 ~ 768)
/***************** breakpoint *****************/
@media only screen and (max-width: 1320px) {
}

/***************** tablet *****************/
@media only screen and (max-width: 1023px) {
  #wrap {
    overflow-x: hidden;
  }
  .header .menu-all {
    display: none;
  }
  .header-util .util-left {
    display: none;
  }
  .headerFixed {
    top: 0px;
  }
  .headerFixed .header-inner {
    border-bottom: 1px solid #cccccc;
  }

  /*header*/
  .header {
    height: auto;
    padding-top: 0px;
  }
  .header .header-inner {
    width: 100%;
    max-width: 100%;
    height: 60px;
    border-bottom: 1px solid #cccccc;
  } /* 0320 수정 */
  .header .logo {
    top: 50%;
    transform: translateY(-50%);
    left: 10px;
  }
  .header .logo a {
    width: 186px;
    background-position: left center;
  }

  /* mo-gnb */
  .header .mo-gnb-open {
    display: block;
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    right: 10px;
    width: 32px;
    height: 32px;
    z-index: 110;
  }
  .header .mo-gnb-open button {
    display: block;
    width: 100%;
    height: 100%;
    background: red url(/static/cpa/img/icon-menu-all.png) no-repeat center;
    background-size: 100% auto;
  }
  .header .mo-gnb-close {
    display: block;
    position: absolute;
    top: 16px;
    right: 10px;
    width: 28px;
    height: 28px;
    z-index: 61;
  }
  .header .mo-gnb-close button {
    display: block;
    width: 100%;
    height: 100%;
    background: url(/static/cpa/img/mo-gnb-close.png) no-repeat center center;
    background-size: 100%;
  }
  .header .mo-gnb-bg {
    display: none;
    position: fixed;
    z-index: 500;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background-color: rgba(0, 0, 0, 0.7);
  }
  .header .pc-gnb-bg,
  .header .pc-gnb-bg.on {
    display: none;
    background: none;
    transition: none;
  }

  /* gnb-box */
  .header .gnb-box {
    display: none;
    position: fixed;
    top: 0px;
    right: 0px; /* right:-336px; */ /* transition-duration: 0.3s; */
    z-index: 501;
  }
  .header .gnb-box > .gnb-box-inner {
    position: relative;
    width: 300px;
    min-height: auto;
    background-color: #fff;
    height: 100vh;
    overflow-y: scroll;
  }

  /* header-util */
  .header-util {
    position: static;
    top: 0px;
    left: 0px;
    width: 100%;
    height: 104px;
    border-bottom: none;
    z-index: 60;
    background-color: #1d68dc;
  }
  .header-util .header-util-inner {
    width: 100%;
    max-width: 100%;
  }
  .header-util .util-member {
    flex-wrap: wrap;
    align-items: flex-end;
    width: 100%;
    padding: 16px 20px;
  }
  .btn-util-member {
    flex: none;
    width: auto;
    height: auto;
  }
  .btn-util-member:first-of-type::after {
    display: none;
  }
  .btn-util-member.join {
    width: 100%;
    margin-bottom: auto;
  }
  .btn-util-member.name {
    width: 100%;
    margin-bottom: auto;
    display: block;
    color: #fff;
  }
  .btn-util-member.name span,
  .btn-util-member.join span {
    font-size: 1.8rem;
    font-weight: 700;
    line-height: 2.8rem;
  }

  /* .btn-util-member > a::before {width: 18px; height: 18px;}
.btn-util-member::after {margin: 0 8px;} */
  .btn-util-member.mydoc > a::before,
  .btn-util-member.mymod > a::before {
    display: none;
  }

  /* gnb(menu) */
  .gnb-box .gnb {
    display: block;
    width: 100%;
    background: #fff;
  }

  /*gnb > depth1*/
  .gnb-box .gnb > ul {
    position: static;
    top: 0px;
    left: 0px;
    width: 100%;
    padding: 12px 20px;
  }
  .gnb-box .gnb > ul li.depth1 {
    float: none;
    border-bottom: 1px solid #eaeaea;
  }
  .gnb-box .gnb > ul li.depth1:last-of-type {
    margin-bottom: 100px;
  }
  .gnb-box .gnb > ul li.depth1.menu1 {
    margin-left: 0px;
  }
  .gnb-box .gnb > ul li.depth1 > a.tit {
    padding: 12px 0;
    margin: 0px;
    height: auto;
    font-size: 1.8rem;
    line-height: 2.8rem;
    color: #333;
    font-weight: 500;
    background: url(/static/cpa/img/icon-gnb-1dep.png) no-repeat right center;
    background-size: 28px !important;
  }
  .gnb-box .gnb > ul li.depth1 > a.tit.on {
    color: #1d68dc;
    background: url(/static/cpa/img/icon-gnb-1dep-on.png) no-repeat right center;
  }
  .gnb-box .gnb > ul li.depth1 > a.tit.on::after {
    display: none;
  }
  /* .gnb-box .gnb > ul li.depth1 > a.tit > span.on{ border-bottom: 5px solid #1e58af; } */

  /*gnb > 좌측타이틀*/
  .gnb-box .gnb .top2m {
    display: none;
    position: static;
    background: none;
    border-bottom: 0px;
  }
  .gnb-box .gnb .top2m .top2m-inner {
    width: 100%;
    max-width: 100%;
  }
  .gnb-box .gnb .top2m .top2m-inner .top2m-title {
    float: none;
    display: none;
  }

  /*gnb > depth2*/
  .gnb-box .gnb .top2m .top2m-inner ul.top2m-list {
    float: none;
    min-height: auto;
    width: 100%;
    border-left: none;
    padding: 0 12px;
    background: none;
    background-color: #f8fafb;
    transition: none;
    box-sizing: border-box;
  }
  .gnb-box .gnb .top2m .top2m-inner ul.top2m-list li.depth2 {
    float: none;
    position: static;
    left: 0px;
    top: 0px;
    right: 0px;
    bottom: 0px;
    padding: 0px;
    margin: 0px;
    width: auto;
  }
  .gnb-box .gnb .top2m .top2m-inner ul.top2m-list li a.depth2-tit {
    position: static;
    display: block;
    padding: 12px 0;
    font-size: 1.4rem;
    font-weight: 400;
    color: #000;
    background-color: #f8fafb;
    border-top: 1px dashed #eaeaea;
    white-space: nowrap;
    transition: none;
  }
  .gnb-box
    .gnb
    .top2m
    .top2m-inner
    ul.top2m-list
    li:first-of-type
    a.depth2-tit {
    border-top: 0px;
  }
  .gnb-box .gnb .top2m .top2m-inner ul.top2m-list li a.depth2-tit.plus {
    background: url(/static/cpa/img/icon-gnb-2dep.png) no-repeat right center;
    background-size: 24px !important;
    background-color: #f8fafb;
  }
  .gnb-box .gnb .top2m .top2m-inner ul.top2m-list li a.depth2-tit.plus.on {
    background: url(/static/cpa/img/icon-gnb-2dep-on.png) no-repeat right center;
  }
  /* .gnb-box .gnb .top2m .top2m-inner ul.top2m-list li+li{ border-top:1px dashed #EAEAEA;} */

  .gnb-box .gnb .menu3 .top2m .top2m-inner ul.top2m-list li.depth2 {
    margin-right: 0;
  }

  /*gnb > depth3*/
  .gnb-box .gnb .top2m .top2m-inner ul.top2m-list li .top3m {
    display: none;
    border-top: 1px dashed #eaeaea;
  }
  .gnb-box .gnb .top2m .top2m-inner ul.top2m-list li .top3m ul {
    margin-top: 0px;
    padding: 12px 0;
  }
  .gnb-box .gnb .top2m .top2m-inner ul.top2m-list li .top3m ul li {
    margin-top: 8px;
  }
  .gnb-box
    .gnb
    .top2m
    .top2m-inner
    ul.top2m-list
    li
    .top3m
    ul
    li:first-of-type {
    margin-top: 0px;
  }
  .gnb-box .gnb .top2m .top2m-inner ul.top2m-list li .top3m ul li a {
    display: block;
    font-weight: 400;
    color: #666;
  }
  .gnb-box .gnb .top2m .top2m-inner ul.top2m-list li .top3m ul li a:hover {
    color: #1d68dc;
    font-weight: 500;
  }
}

/***************** mobile *****************/
@media only screen and (max-width: 767px) {
}

 

 

jQuery

전체를 감싸는 #wrap 크기(width)를 감지하여 Wide, Tablet, Mobile 클래스를 부여합니다.

$(document).ready(function () {
  /*************** 창크기감지******************/
  var windowWidth = $("#wrap").width();
  KindOfEquipment = 3;

  if (windowWidth > 1024) {
    if ($("body").find(".Wide").length) {
      $("#wrap").removeClass("Tablet");
      $("#wrap").removeClass("Mobile");
      /* headerFixed(40, 120) //0320 삭제 */
    } else {
      $("#wrap").removeClass("Tablet");
      $("#wrap").removeClass("Mobile");
      $("#wrap").addClass("Wide");
    }
  }
  if (windowWidth <= 1024 && windowWidth > 768) {
    if ($("body").find(".Tablet").length) {
      $("#wrap").removeClass("Wide");
      $("#wrap").removeClass("Mobile");
    } else {
      $("#wrap").removeClass("Wide");
      $("#wrap").removeClass("Mobile");
      $("#wrap").addClass("Tablet");
      /* headerFixed(0, 60) //0320 삭제 */
    }
  }
  if (windowWidth <= 768) {
    if ($("body").find(".Mobile").length) {
      $("#wrap").removeClass("Wide");
      $("#wrap").removeClass("Tablet");
    } else {
      $("#wrap").removeClass("Wide");
      $("#wrap").removeClass("Tablet");
      $("#wrap").addClass("Mobile");
      /* headerFixed(0, 60) //0320 삭제 */
    }
  }

  if ($("#wrap").is(".Wide")) {
    KindOfEquipment = 3;
  }
  if ($("#wrap").is(".Tablet")) {
    KindOfEquipment = 2;
  }
  if ($("#wrap").is(".Mobile")) {
    KindOfEquipment = 1;
  }

  ResizeSituation = 0;
  $(window).resize(function () {
    $("body").removeClass("menu-on");
    var windowWidth = $("#wrap").width();

    if (windowWidth <= 1024 && windowWidth > 768) {
      if (ResizeSituation != 1) {
        if ($("body").find(".Tablet").length) {
          $("#wrap").removeClass("Wide");
          $("#wrap").removeClass("Mobile");
        } else {
          $("#wrap").removeClass("Wide");
          $("#wrap").removeClass("Mobile");
          $("#wrap").addClass("Tablet");
        }
        $(".header .gnb-box").css(
          "display",
          "none"
        ); /* 0320 - 모바일헤더 수정 */
        $(".mo-gnb-bg").hide();
        // $('.gnb-box .gnb > ul li.depth1 > a.tit').removeClass('on');
        /* headerFixed(0, 60) //0320 삭제 */
        ResizeSituation = 1;
        KindOfEquipment = 2;
      } else {
        return false;
      }
    }
    if (windowWidth <= 768) {
      if (ResizeSituation != 2) {
        if ($("body").find(".Mobile").length) {
          $("#wrap").removeClass("Wide");
          $("#wrap").removeClass("Tablet");
        } else {
          $("#wrap").removeClass("Wide");
          $("#wrap").removeClass("Tablet");
          $("#wrap").addClass("Mobile");
        }
        /* headerFixed(0, 60) //0320 삭제 */
        $(".header .gnb-box").css(
          "display",
          "none"
        ); /* 0321 - 모바일헤더 추가 */
        ResizeSituation = 2;
        KindOfEquipment = 1;
      } else {
        return false;
      }
    }
    if (windowWidth > 1024) {
      if (ResizeSituation != 3) {
        if ($("body").find(".Wide").length) {
          $("#wrap").removeClass("Tablet");
          $("#wrap").removeClass("Mobile");
        } else {
          $("#wrap").removeClass("Tablet");
          $("#wrap").removeClass("Mobile");
          $("#wrap").addClass("Wide");
        }
        $(".header .gnb-box").css(
          "display",
          "block"
        ); /* 0320 - 모바일헤더 수정 */
        $(".top2m").css({ display: "none" });
        $(".top3m").removeAttr("style");
        $(".tabBox .list").show(); //서브 탭메뉴
        $("body").removeClass("popup-on");
        /* headerFixed(40, 120) //0320 삭제 */
        ResizeSituation = 3;
        KindOfEquipment = 3;
      } else {
        return false;
      }
    }
  });

  /************GNB-PC용******************/
  var currGnb_index = null;
  //gnb 열기
  $(".gnb .depth1")
    .children("a")
    .on({
      "mouseenter focusin": function () {
        if (KindOfEquipment == 3) {
          if ($(this).parent().index() != currGnb_index) {
            //현재 대메뉴 에서 다시 마우스 오버시 깜박임 방지
            $(".top2m").hide();
            $(".gnb .depth1").removeClass("on");
            $(this).parent().addClass("on");

            var HH = $(this).siblings(".top2m").height();
            //alert("기본 받은값"+HH);

            $(this)
              .siblings(".top2m")
              .find(".top2m-title")
              .css("height", HH); /*좌우 높이 같게 */
            $(".pc-gnb-bg")
              .addClass("on")
              .css("height", HH + 2);
            $(this).siblings(".top2m").stop().show();

            currGnb_index = $(this).parent().index();
          }
        }
      }
    });

  //gnb닫기
  $(".gnb").on({
    mouseleave: function () {
      if (KindOfEquipment == 3) {
        $(".top2m").hide();
        $(".gnb .depth1").removeClass("on");
        $(".pc-gnb-bg").removeClass("on").css("height", "0");

        currGnb_index = null;
      }
    }
  });

  //gnb(pc) 마지막 focusout 처리
  var $gnbLast3m = $(".gnb > ul > li:last-child")
    .prev()
    .find(".top2m-list > li:last-child > .top3m ul li:last-child"); //0203 수정
  var $gnbLast2m = $(".gnb > ul > li:last-child")
    .prev()
    .find(".top2m-list > li:last-child"); //0203 수정
  if ($gnbLast3m.length) {
    $gnbLast3m.on({
      focusout: function () {
        if (KindOfEquipment == 3) {
          $(".top2m").hide();
          $(".gnb .depth1").removeClass("on");
          $(".pc-gnb-bg").css("height", "0");
          currGnb_index = null;
        }
      }
    });
  } else {
    $gnbLast2m.on({
      focusout: function () {
        if (KindOfEquipment == 3) {
          $(".top2m").hide();
          $(".gnb .depth1").removeClass("on");
          $(".pc-gnb-bg").css("height", "0");
          currGnb_index = null;
        }
      }
    });
  }

  //모바일 뎁스1 클릭
  $(".gnb a.tit").on("click", function (event) {
    var $target = $(event.target);
    if (KindOfEquipment <= 2) {
      if ($target.is(".on")) {
        $(this).removeClass("on").next(".menuBox").slideUp();
        //return false;
      } else {
        $(".gnb a.tit").removeClass("on").next(".menuBox").slideUp();
        $(this).addClass("on").next(".menuBox").slideDown();
        $(".top3m").removeClass("on").hide();
      }
      return false;
    } else {
      return true;
    }
  });
  //모바일 뎁스2 클릭
  $(".depth2 a.plus").on("click", function () {
    if (KindOfEquipment <= 2) {
      var Top3OnOff = $(this).is(".on");
      if (Top3OnOff == true) {
        $(this).siblings(".top3m").removeClass("on").slideUp();
        $(this).removeClass("on");
        return false;
      } else if (Top3OnOff == false) {
        $(".depth2 a.plus").not($(this)).removeClass("on");
        $(this).addClass("on");
        $(".top3m").not($(this).siblings()).removeClass("on").slideUp();
        $(this).siblings(".top3m").addClass("on").slideDown();
        return false;
      }
    }
  });

  /************모바일 GNB 열기/닫기******************/
  $(".mo-gnb-open > button").on("click", function () {
    $("body").addClass("popup-on");
    // $('body').css({'overflow' : 'hidden'});
    $(".mo-gnb-bg").show();
    $(".header .gnb-box").css("display", "block"); /* 0320 - 모바일헤더 수정 */
    // $('.gnb-box .gnb > ul li.depth1 > a.tit').removeClass('on');

    if ($(".gnb").find(".tit.on").length) {
      var $titOn = $(".gnb").find(".tit.on");
      $titOn.parent(".depth1").find(".top2m").css({ display: "block" });
      console.log($titOn);
    } else {
    }

    return false;
  });

  $(".mo-gnb-close > button , .mo-gnb-bg").on("click", function () {
    $("body").removeClass("popup-on");
    // $('body').css({'overflow' : 'initial'});
    /* $('.gnb a.tit').removeClass('on').next('.menuBox').hide();
		$('.top3m').removeClass('on').hide(); */

    $(".header .gnb-box").css("display", "none"); /* 0320 - 모바일헤더 수정 */
    $(".mo-gnb-bg").hide();
    return false;
  });

  /************전체메뉴보기******************/

  $(".btn-menu-open > button").on("click", function () {
    /* if ($('body').find('.headerFixed').length) { //0320 삭제
			$(this).closest('.menu-all').find('.menu-list').css({'top' : 81});
		} else {
			$(this).closest('.menu-all').find('.menu-list').css({'top' : 121});
		} */

    $(this).closest(".menu-all").find(".menu-list").css({ top: 121 }); //0320 추가
    $("body").addClass("menu-on");
    $(".menu-all-layer").show();
    $(".btn-menu-close > button").focus();
    return false;
  });

  var $menuLast3m = $(".menu-list-inner > ul > li:last-child").find(
    ".depth2 > li:last-child > .depth3 ul li:last-child"
  );
  var $menuLast2m = $(
    ".menu-list-inner > ul > li:last-child .depth2 > li:last-child"
  );
  if ($menuLast3m.length) {
    $gnbLast3m.on({
      focusout: function () {
        if (KindOfEquipment == 3) {
          $(".btn-menu-close > button").focus();
        }
      }
    });
  } else {
    $menuLast2m.on({
      focusout: function () {
        if (KindOfEquipment == 3) {
          $(".btn-menu-close > button").focus();
        }
      }
    });
  }

  $(".btn-menu-close > button").on("click", function () {
    $(".menu-all-layer").hide();
    $(".btn-menu-open > button").focus();
    $("body").removeClass("menu-on");
    return false;
  });
});

 

마치며...

이번 포스팅에서는 반응형 웹(Responsive Web)에서 PC용 GNB와 모바일 햄버거 메뉴(mobile burger menu) 두 곳에서 다 사용할 수 있는 HTML5 + CSS3 + jQuery코드로 이웃님들을 찾아왔습니다. 다음 포스팅에서는 ES6문법에 대해서 설명해 드릴려고 합니다.


함께 읽으면 좋은 글

 

 

제이쿼리 jQuery 햄버거 메뉴 만들기

제이쿼리로 만드는 모바일 햄버거 메뉴 당연한 얘기지만 제이쿼리를 기본적으로 불러와야한다. CDN방식으로 사용하면 펜하겠지만 로딩 이슈가 있어서 서버에 올린 상태를 기반으로 코드를 작성

miroiter.tistory.com

 

반응형