ご質問・お見積り等お気軽にご相談ください
お問い合わせ

【iOS対応】究極のハンバーガーメニュー(スライドメニュー)

【iOS対応】究極のハンバーガーメニュー(スライドメニュー)

システム開発部のヤマモトです。
スマホサイトでよく実装されている横からスライドするメニュー(ハンバーガーメニュー)の実装についての記事です。
メニューを開いたときにCSSでoverflow:hidden;を指定してもiOS safariでコンテンツ部分がスクロールしてしまう問題を回避しつつスムーズなスライドを実現したスライドメニューの完成形ともいえるメニューです。

仕様

・開閉時メニューの横幅分とコンテンツを外にスクロール
・メニューを開いたときにコンテンツ部分がスクロールしない(iPhoneも)
・開閉ボタンをスクロール追従させる
・できるだけCSSで完結する。

説明

デモ

開閉時の挙動

チェックボックスのオンオフで開閉する仕組みで、開閉部分はCSSのみで動作しています。

<input type="checkbox" id="nav-tgl">
<label for="nav-tgl" class="open nav-tgl-btn"><span></span></label>

 


/*メニュー部分*/
.drower-menu {
transform: translateX(100%);
}
#nav-tgl:checked ~ .drower-menu {
transform: none;
}

/*コンテンツ部分*/
.content-wrapper {
transition: transform .6s cubic-bezier(0.215, 0.61, 0.355, 1);
}
#nav-tgl:checked ~ .content-wrapper {
transform: translateX(-250px);
}
上記labelタグのクリックでチェックボックスのオンオフを検知して
CSSで指定されたメニューとコンテンツのスクロールが実行されます。

メニューを開いた時コンテンツをスクロールさせない


$(function(){
  $('#nav-tgl').on('change', function(){
    var st = $(window).scrollTop();
    if($(this).prop("checked") == true) {
      $('html').addClass('scroll-prevent');
      $('html').css('top', -(st) + 'px');
      $('#nav-tgl').on('change', function(){
        if($(this).prop("checked") !== true) {
          $('html').removeClass('scroll-prevent');
          $(window).scrollTop(st);
        }
      });
    }
  });
});



.scroll-prevent {
position: fixed;
z-index: -1;
width: 100%;
height: 100%;
}

メニューを開いたときhtmlタグのpositionをCSSで絶対位置にし、現在のスクロール位置をjavascriptで指定することでコンテンツの位置を固定しています。
Chrome等のブラウザではhtmlタグに対してoverflow:hidden;を指定することでコンテンツのスクロールを制限することができますがiOS(safari)ではスクロール位置を指定しないと固定することができませんでした。

ソースコード

HTML

<input type="checkbox" id="nav-tgl">
<label for="nav-tgl" class="open nav-tgl-btn"><span></span></label>
<label for="nav-tgl" class="close nav-tgl-btn"></label>
<div class="content-wrapper">
<div class="site-content">
コンテンツ<br>
</div>
</div>
<div class="drower-menu">
<div class="drower-menu-list">
<a href="#">メニュー1</a>
<a href="#">メニュー2</a>
<a href="#">メニュー3</a>
<a href="#">メニュー4</a>
<a href="#">メニュー5</a>
</div>
</div>
CSS


.scroll-prevent {
position: fixed;
z-index: -1;
width: 100%;
height: 100%;
}
#nav-tgl {
display: none;
}
.nav-tgl-btn {
cursor: pointer;
position: fixed;
top: 0;
right: 0;
margin: 0;
}
.open {
z-index: 2;
width: 48px;
height: 48px;
background: #000;
transition: background .6s, transform .6s cubic-bezier(0.215, 0.61, 0.355, 1);
}
.open::before,
.open::after {
content: "";
}
.open span,
.open::before,
.open::after {
content: "";
position: absolute;
top: calc(50% - 1px);
right: 30%;
width: 40%;
border-bottom: 2px solid white;
transition: transform .6s cubic-bezier(0.215, 0.61, 0.355, 1);
}
.open::before {
transform: translateY(-8px);
}
.open::after {
transform: translateY(8px);
}
.close {
z-index: 1;
width: 100%;
height: 100%;
pointer-events: none;
transition: background .6s;
}
#nav-tgl:checked + .open {
background: #dd4884;
transform: translateX(-250px);
}
#nav-tgl:checked + .open span {
transform: scaleX(0);
}
#nav-tgl:checked + .open::before {
transform: rotate(45deg);
}
#nav-tgl:checked + .open::after {
transform: rotate(-45deg);
}
#nav-tgl:checked ~ .close {
pointer-events: auto;
background: rgba(0,0,0,.3);
}
.content-wrapper {
transition: transform .6s cubic-bezier(0.215, 0.61, 0.355, 1);
}
#nav-tgl:checked ~ .content-wrapper {
transform: translateX(-250px);
}
/* メニューデザイン */
.drower-menu {
z-index: 999;
position: fixed;
overflow: auto;
-webkit-overflow-scrolling: touch;
overflow-scrolling: touch;
top: 0;
right: 0;
width: 250px;
height: 100%;
margin: 0;
padding: 0 0 10px;
box-sizing: border-box;
background: #333;
transform: translateX(100%);
transition: transform .6s cubic-bezier(0.215, 0.61, 0.355, 1);
}
.drower-menu a {
display: block;
color: white;
padding: 10px;
text-decoration: inherit;
transition: background .6s;
font-size: 1.4rem;
}
.drower-menu a:hover {
background: black;
}
#nav-tgl:checked ~ .drower-menu {
transform: none;
}
Javascript

$(function(){
  $('#nav-tgl').on('change', function(){
    var st = $(window).scrollTop();
    if($(this).prop("checked") == true) {
      $('html').addClass('scroll-prevent');
      $('html').css('top', -(st) + 'px');
      $('#nav-tgl').on('change', function(){
        if($(this).prop("checked") !== true) {
          $('html').removeClass('scroll-prevent');
          $(window).scrollTop(st);
        }
      });
    }
  });
});