【iOS対応】究極のハンバーガーメニュー(スライドメニュー)
投稿日:2019年9月6日 / 更新日:2019年9月12日
システム開発部のヤマモトです。
スマホサイトでよく実装されている横からスライドするメニュー(ハンバーガーメニュー)の実装についての記事です。
メニューを開いたときに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で指定されたメニューとコンテンツのスクロールが実行されます。
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)ではスクロール位置を指定しないと固定することができませんでした。
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);
}
});
}
});
});