どうも、くまだです。
ハンバーガーメニュー開いた状態でスクロールし、ハンバーガーメニューを閉じると背景がスクロールされてしまう。そうならないよう、ハンバーガーメニュー開いているときは背景固定するやり方です。
ハンバーガーメニュー開いたとき、背景固定する方法
例えば、以下のように作ると、背景はスクロールできてしまいます。
<div class="l-container">
<header class="l-header p-header" id="js-header">
<div class="p-header__inner">
<h1 class="p-header__title" id="logo-label">
<a href="" aria-labelledby="logo-label">ロゴ</a>
</h1>
<div class="p-header__hamburger">
<button class="c-hamburger" aria-expanded="false" aria-label="メニューを開く">
</button>
</div>
<nav class="p-header__nav p-nav" aria-expanded="false">
<div class="p-nav__inner">
<ul class="p-nav__list">
<li class="p-nav__item">
<a href="#" class="p-nav__link">メニュー</a>
</li>
<li class="p-nav__item">
<a href="#" class="p-nav__link">メニュー</a>
</li>
<li class="p-nav__item">
<a href="#" class="p-nav__link">メニュー</a>
</li>
<li class="p-nav__item">
<a href="#" class="p-nav__link">メニュー</a>
</li>
</ul>
</div>
</nav>
</div>
</header>
<main>
<div class="main" id="js-mainVisual">メインビジュアル</div>
<div class="contents">メインコンテンツ</div>
</main>
</div>
ul,
li {
list-style: none;
}
p {
margin: 0;
}
a {
text-decoration: none;
}
.main {
padding-top: 72px;
background: skyblue;
height: 700px;
}
.contents {
height: 2000px;
background-color: orange;
}
.l-header {
display: block;
z-index: 999;
position: fixed;
top: 0;
right: 0;
left: 0;
width: 100%;
height: 72px;
background-color: #fff;
}
.l-header.change-color {
background-color: lightcoral;
}
.c-hamburger {
position: relative;
width: inherit;
height: inherit;
margin: 0;
cursor: pointer;
background-color: lightblue;
}
.c-hamburger::after {
position: absolute;
content: '';
width: 6px;
height: 6px;
background: red;
display: block;
left: calc(50% - 4px);
top: calc(50% - 4px);
box-shadow: -12px 0 0 orange, 12px 0 0 orange, -12px -12px 0 orange, 0 -12px 0 orange, 12px -12px 0 orange, -12px 12px 0 orange, 0 12px 0 orange, 12px 12px 0 orange;
transition: box-shadow .4s;
}
.c-hamburger::before {
opacity: 0;
content: "";
position: absolute;
width: 40px;
height: 1px;
background: orange;
transform: translate(-50%, -50%) rotate(135deg);
top: 50%;
left: 50%;
transition: opacity .4s;
}
.c-hamburger[aria-expanded="true"]::before {
opacity: 1;
}
.c-hamburger[aria-expanded="true"]::after {
box-shadow: initial;
content: "";
position: absolute;
width: 40px;
height: 1px;
background: orange;
transform: translate(-50%, -50%) rotate(45deg);
top: 50%;
left: 50%;
}
.p-header__nav {
display: flex;
z-index: 10;
position: absolute;
top: 0;
right: -100%;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100vh;
background: transparent;
font-weight: 700;
opacity: 0;
transition: 0.6s;
}
@media screen and (min-width:768px) {
.p-header__nav {
position: static;
height: 100%;
opacity: initial;
align-items: flex-end;
}
}
.p-header__inner {
display: flex;
align-items: center;
justify-content: space-between;
height: inherit;
padding: 13px 20px;
}
.p-header__title {
width: 100%;
max-width: 120px;
height: 48px;
background: lightgray;
}
.p-header__title a {
display: block;
width: 100%;
height: auto;
}
.p-header__title a img {
height: 100%;
}
.p-header__hamburger {
z-index: 100;
position: absolute;
top: 0;
right: 0;
width: 95px;
height: 100%;
}
@media screen and (min-width:768px) {
.p-header__hamburger {
display: none;
}
}
.p-header__nav[aria-expanded="true"] {
position: fixed;
top: 0;
right: 0;
background: lightblue;
opacity: 1;
transition: 0.6s;
}
.p-nav {
padding-top: 72px;
padding-bottom: 72px;
}
@media screen and (min-width:768px) {
.p-nav {
padding-top: 16px;
padding-bottom: 16px
}
}
.p-nav__list {
display: block;
width: 100%;
padding-right: 20px;
padding-left: 20px;
background: lightblue;
}
@media screen and (min-width:768px) {
.p-nav__list {
display: flex;
background: inherit;
padding-right: 0;
}
}
.p-nav__item {
position: relative;
width: 100%;
}
.p-nav__link {
display: block;
width: 100%;
height: 100%;
padding: 20px;
text-align: center;
}
window.addEventListener('DOMContentLoaded', () => {
const hamburger = document.querySelector('.c-hamburger');
const nav = document.querySelector('.p-header__nav');
hamburger.addEventListener('click', () => {
const hamburgerAriaValue = hamburger.getAttribute('aria-expanded');
if (hamburgerAriaValue === 'false') {
hamburger.setAttribute('aria-expanded', true);
nav.setAttribute('aria-expanded', true);
} else {
hamburger.setAttribute('aria-expanded', false);
nav.setAttribute('aria-expanded', false);
}
});
});
ハンバーガーメニューを開いて、スクロール。ハンバーガーメニューを閉じると、スクロールした分背景のコンテンツがスクロールされた状態になります。場合によっては、ハンバーガーメニューを開いた状態は背景を固定させたいときもあります。
JavaScriptは次のように修正します。
document.addEventListener('DOMContentLoaded', function () {
const hamburger = document.querySelector('.c-hamburger');
const headHam = document.querySelector('.p-header__hamburger');
const headNav = document.querySelector('.p-header__nav');
const navItem = document.querySelector('.p-nav__item');
const body = document.querySelector('body');
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
let pos;
function resetStyles() {
body.style.overflow = 'initial';
body.style.position = 'static';
body.style.width = 'initial';
hamburger.setAttribute('aria-expanded', false);
headNav.setAttribute('aria-expanded', false);
headNav.style.marginRight = '0';
headHam.style.marginRight = '0';
body.style.marginRight = '0';
window.scrollTo(0, pos);
}
function openStyle(){
hamburger.setAttribute('aria-expanded', true);
headNav.setAttribute('aria-expanded', true);
pos = window.scrollY;
body.style.overflow = 'hidden';
body.style.position = 'fixed';
body.style.width = `calc(100% - ${scrollbarWidth}px)`;
body.style.top = -pos + 'px';
body.style.marginRight = `${scrollbarWidth}px`;
headNav.style.marginRight = `${scrollbarWidth}px`;
headHam.style.marginRight = `${scrollbarWidth}px`;
}
hamburger.addEventListener('click', () => {
const hamburgerAriaValue = hamburger.getAttribute('aria-expanded');
if (hamburgerAriaValue === 'true') {
resetStyles();
} else {
openStyle();
}
});
headNav.addEventListener('click', resetStyles);
navItem.addEventListener('click', resetStyles);
});
ハンバーガーメニューを開いた状態でスクロールしても、背景は固定されたままです。ハンバーガーメニューを閉じても、背景はハンバーガーメニューを開いた時点の位置にいます。
各要素を取得し、それぞれ変数に格納します。
const hamburger = document.querySelector('.c-hamburger');
const headHam = document.querySelector('.p-header__hamburger');
const headNav = document.querySelector('.p-header__nav');
const navItem = document.querySelector('.p-nav__item');
const body = document.querySelector('body');
スクロールバーの幅を計算します。これは背景固定時にレイアウトがカクっとなるのを防止するため、あとで必要になります。これも変数に格納します。
// スクロールバーの幅を計算
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
スクロール位置を保持する変数を設定します。
// スクロール位置を保持する
let pos;
ハンバーガーメニューを開くときのスタイルを設定する関数を用意します。現在のスクロール位置を変数に格納。また、スクロールバーの幅を計算した変数もそれぞれ設定していきます。
function openStyle(){
hamburger.setAttribute('aria-expanded', true); // ハンバーガーメニューの状態を開いた状態に設定
headNav.setAttribute('aria-expanded', true); // ヘッダーナビゲーションの状態を開いた状態に設定
// 現在のスクロール位置
pos = window.scrollY;
// ページ全体を固定し、スクロールバーの幅を考慮して幅と余白を設定
body.style.overflow = 'hidden';
body.style.position = 'fixed';
body.style.width = `calc(100% - ${scrollbarWidth}px)`;
body.style.top = -pos + 'px';
body.style.marginRight = `${scrollbarWidth}px`;
headNav.style.marginRight = `${scrollbarWidth}px`;
headHam.style.marginRight = `${scrollbarWidth}px`;
}
例えば、scrollbarWidthの値が20とかなら、`${scrollbarWidth}px`の部分は20pxとなります。同様に、posの値が1000とかなら、-pos + ‘px’は-1000pxとなります。
同様に、スタイルを初期状態に戻す関数を用意します。
// スタイルを初期状態にリセットする関数
function resetStyles() {
body.style.overflow = 'initial';
body.style.position = 'static';
body.style.width = 'initial';
hamburger.setAttribute('aria-expanded', false); // ハンバーガーメニューの状態を閉じた状態に設定
headNav.setAttribute('aria-expanded', false); // ヘッダーナビゲーションの状態を閉じた状態に設定
headNav.style.marginRight = '0';
headHam.style.marginRight = '0';
body.style.marginRight = '0';
window.scrollTo(0, pos); // スクロール位置を元に戻す
}
ハンバーガーメニューをクリックしたときのイベントを設定します。上記で設定した関数をここで使います。
hamburger.addEventListener('click', () => {
const hamburgerAriaValue = hamburger.getAttribute('aria-expanded');
if (hamburgerAriaValue === 'true') {
// メニューが開いている場合、スタイルをリセット
resetStyles();
} else {
// メニューが閉じている場合、メニューを開くスタイルを設定
openStyle();
}
});
現状だと、ハンバーガーメニューをクリックしたときのみ、ハンバーガーメニューが閉じる仕様になっているので、ナビメニューとボディ部分クリックした場合でもハンバーガーメニュー閉じるようにします。
headNav.addEventListener('click', resetStyles);
navItem.addEventListener('click', resetStyles);
これでメニューとボディ部分をクリックしてもハンバーガーメニューが閉じます。
ちなみに検証ツールで確認すると、bodyタグにスクロール位置や右側余白(背景固定時のスクロール幅分)の値などが確認できます。
ここまで読んでくださりありがとうございました。