JavaScript攻略 ハンバーガーメニュー
ハンバーガーメニューだけでもコードはこんなにたくさんある。
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<link rel="stylesheet" href="style.css" />
<script src="script.js" defer></script>
<title>第3週まとめ</title>
</head>
<body>
<header>
<button class="menu-button js-menu-button" id="menuButton">
<img
src="images/26971246.png"
alt="メニュー"
class="hamburger-menu js-hamburger-menu"
/>
</button>
<div class="menu-modal js-menu-modal" id="menuModal">
<button class="close-button js-close-button" id="closeButton">x</button>
<nav class="modal-nav-menu js-modal-nav-nemu" id="navMenu">
<ul>
<li><a href="#">Products</a></li>
<li><a href="#">About Us</a></li>
<li><a href="#">Dealer Locator</a></li>
</ul>
</nav>
</div>
</header>
</body>
</html>
css
#menuButton {
position: fixed;
top: 20px;
right: 20px;
width: 44px;
height: 44px;
padding: 0;
border: none;
background: transparent;
z-index: 1001;
cursor: pointer;
}
/* メニューが開いたら、ハンバーガーPNGを非表示にする */
#menuButton.is-hidden {
opacity: 0;
pointer-events: none;
}
.hamburger-menu {
width: 100%;
height: 100%;
object-fit: contain;
display: block;
}
/* 全画面オーバーレイ */
.menu-modal {
position: fixed;
inset: 0;
background: rgba(10, 10, 10, 0.94);
opacity: 0;
visibility: hidden;
transition:
opacity 0.8s ease,
visibility 0.8s ease;
z-index: 1000;
}
/* 開いた状態 */
.menu-modal.is-open {
opacity: 1;
visibility: visible;
}
/* 閉じるボタン */
.close-button {
position: fixed;
top: 24px;
right: 24px;
border: none;
background: transparent;
color: #fff;
font-size: 32px;
line-height: 1;
cursor: pointer;
z-index: 1005;
}
/* メニューの中央配置 */
.modal-nav-menu {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
/* リストの初期化 */
.modal-nav-menu ul {
list-style: none;
padding: 0;
margin: 0;
text-align: center;
}
/* リスト項目:最初は下にずらして透明 */
.modal-nav-menu li {
opacity: 0;
transform: translateY(30px);
transition:
opacity 0.8s ease,
transform 0.8s ease;
margin: 28px 0;
}
/* モーダルが開いたら、リストが上がりながら表示 */
.menu-modal.is-open .modal-nav-menu li {
opacity: 1;
transform: translateY(0);
}
/* 順番に出す */
.menu-modal.is-open .modal-nav-menu li:nth-child(1) {
transition-delay: 0.1s;
}
.menu-modal.is-open .modal-nav-menu li:nth-child(2) {
transition-delay: 0.2s;
}
.menu-modal.is-open .modal-nav-menu li:nth-child(3) {
transition-delay: 0.3s;
}
/* リンク */
.modal-nav-menu a {
color: white;
text-decoration: none;
font-size: 32px;
letter-spacing: 0.08em;
font-weight: 600;
}
/* ホバー時 */
.modal-nav-menu a:hover {
opacity: 0.7;
}
JavaScript
// ハンバーガーメニュー
const menuButton = document.getElementById("menuButton");
const menuModal = document.getElementById("menuModal");
menuButton.addEventListener("click", function () {
menuModal.classList.add("is-open");
menuButton.classList.add("is-hidden");
closeButton.addEventListener("click", function () {
menuModal.classList.remove("is-open");
menuButton.classList.remove("is-hidden");
});
});このCSSは大きく分けると、次の役割があります。
1. ハンバーガーボタンの見た目と位置
2. メニューが開いた時にハンバーガーを隠す
3. PNG画像のサイズ調整
4. 全画面オーバーレイ
5. 閉じるボタン
6. メニューリストの中央配置
7. リストをふわっと順番に表示
8. リンク文字のデザインでは一行ずつ見ていきます。
ハンバーガーボタン
#menuButton {id="menuButton" が付いた要素にCSSを当てます。
HTMLだと、たぶんこれです。
<button class="menu-button js-menu-button" id="menuButton">つまり、ハンバーガーメニューのボタン本体です。
position: fixed;画面に固定します。
スクロールしても、常に同じ位置に表示されます。
ブラウザ画面の右上に固定するための指定です。
top: 20px;画面の上から 20px の位置に配置します。
right: 20px;画面の右から 20px の位置に配置します。
つまり、
右上から少し内側に置くということです。
width: 44px;ボタンの横幅を 44px にします。
height: 44px;ボタンの高さを 44px にします。
これで、ボタンは
44px × 44pxの正方形になります。
スマホでも押しやすいサイズです。
padding: 0;ボタンの内側の余白を消します。
ボタンにはブラウザ標準で少し余白が入ることがあります。
それを消しています。
border: none;ボタンの枠線を消します。
これがないと、ボタンに灰色の枠が出ることがあります。
background: transparent;ボタンの背景を透明にします。
transparent は「透明」という意味です。
z-index: 1001;重なり順を指定しています。
数字が大きいほど前面に出ます。
ここでは、ハンバーガーボタンをかなり前面に表示しています。
cursor: pointer;マウスを乗せた時に、カーソルを指マークにします。
クリックできますよと分かりやすくするためです。
}#menuButton のCSS指定はここまでです。
メニューが開いたら、ハンバーガーPNGを非表示にする
/* メニューが開いたら、ハンバーガーPNGを非表示にする */これはコメントです。
CSSの動作には影響しません。
人間が見た時に、
ここは何のためのCSSか分かりやすくするために書いています。
#menuButton.is-hidden {これは、
id="menuButton" の要素に
is-hidden というクラスが付いた時という意味です。
例えば通常はこうです。
<button id="menuButton">JavaScriptで is-hidden が付くとこうなります。
<button id="menuButton" class="is-hidden">実際にはclassが複数あるので、こういう感じです。
<button class="menu-button js-menu-button is-hidden" id="menuButton"> opacity: 0;透明にします。
見えなくなります。
ただし、要素自体はまだそこに存在しています。
pointer-events: none;クリックできなくします。
opacity: 0; だけだと、見えないのにクリックできてしまうことがあります。
それを防ぐために、
pointer-events: none;を入れています。
つまり、
見えないし、押せないという状態にしています。
}#menuButton.is-hidden の指定はここまでです。
ハンバーガー画像
.hamburger-menu {class="hamburger-menu" が付いた画像にCSSを当てます。
HTMLだとこれです。
<img src="images/26971246.png" alt="メニュー" class="hamburger-menu"> width: 100%;画像の横幅を、親要素いっぱいにします。
親要素は #menuButton です。
#menuButton は 44px なので、画像の横幅も 44px になります。
height: 100%;画像の高さを、親要素いっぱいにします。
つまり、画像も
44px × 44pxの中に収まります。
object-fit: contain;画像の縦横比を保ったまま、枠の中に収めます。
画像が横長・縦長でも、無理に引き伸ばさずに表示します。
画像をつぶさず、きれいに収める指定です。
display: block;画像をブロック要素として扱います。
img は標準だと少し下に余白が出ることがあります。
display: block; にすると、その余白を防ぎやすいです。
※最下部に詳しい説明
}.hamburger-menu の指定はここまでです。
全画面オーバーレイ
/* 全画面オーバーレイ */コメントです。
ここから、画面全体を覆うメニュー部分のCSSです。
.menu-modal {class="menu-modal" が付いた要素にCSSを当てます。
HTMLだとこれです。
<div class="menu-modal js-menu-modal" id="menuModal">これはメニュー全体を包んでいる箱です。
position: fixed;画面に固定します。
スクロールしても、画面全体にかぶさるようにします。
inset: 0;これは、
top: 0;
right: 0;
bottom: 0;
left: 0;と同じ意味です。
つまり、画面全体に広げます。
上も右も下も左も0
↓
画面いっぱいです。
background: rgba(10, 10, 10, 0.94);背景色を指定しています。
rgba は、
red
green
blue
alphaの略です。
rgba(10, 10, 10, 0.94)は、かなり黒に近い色です。
最後の 0.94 は透明度です。
1.0 → 完全に不透明
0.0 → 完全に透明
0.94 → ほぼ黒、少しだけ透けるです。
opacity: 0;最初は透明にします。
つまり、メニューは見えません。
visibility: hidden;見えない状態にします。
opacity: 0; だけだと透明なだけで、要素自体は存在しています。
visibility: hidden; を入れることで、見えない状態として扱います。
transition:ここから、変化をなめらかにする設定を書きます。
opacity 0.8s ease,opacity が変わる時に、0.8秒 かけて変化させます。
つまり、
透明 → 表示がふわっとなります。
ease は、動きに少し緩急をつける指定です。
visibility 0.8s ease;visibility の変化にも 0.8秒 を指定しています。
ただし、実際にふわっと見える主役は opacity です。
visibility は、見える・見えないの状態管理のために使っています。
z-index: 1000;重なり順です。
メニューオーバーレイを前面に出します。
ただし、ハンバーガーボタンは 1001、閉じるボタンは 1005 なので、それらの方がさらに前に出ます。
}.menu-modal の指定はここまでです。
開いた状態
/* 開いた状態 */コメントです。
ここから、メニューが開いた時のCSSです。
.menu-modal.is-open {これは、
menu-modal に is-open が付いた時という意味です。
通常はこうです。
<div class="menu-modal">JavaScriptで is-open が付くとこうなります。
<div class="menu-modal is-open">その時に、このCSSが効きます。
opacity: 1;透明度を 1 にします。
つまり、完全に見える状態にします。
visibility: visible;表示状態にします。
}.menu-modal.is-open の指定はここまでです。
この部分の動きはこうです。
通常時
opacity: 0;
visibility: hidden;
開いた時
opacity: 1;
visibility: visible;JavaScriptで is-open を付けることで、メニューがふわっと表示されます。
閉じるボタン
/* 閉じるボタン */コメントです。
.close-button {class="close-button" が付いた要素にCSSを当てます。
HTMLだとこれです。
<button class="close-button js-close-button" id="closeButton">×</button> position: fixed;閉じるボタンを画面に固定します。
top: 24px;画面上から 24px の位置に置きます。
right: 24px;画面右から 24px の位置に置きます。
つまり、右上に置いています。
border: none;ボタンの枠線を消します。
background: transparent;ボタンの背景を透明にします。
color: #fff;文字色を白にします。
#fff は #ffffff の省略形で、白です。
font-size: 32px;文字サイズを 32px にします。
× のサイズです。
line-height: 1;行の高さを文字サイズとほぼ同じにします。
× が上下にズレにくくなります。
cursor: pointer;マウスを乗せると指マークにします。
z-index: 1005;閉じるボタンをかなり前面に出します。
menu-modal が 1000、menuButton が 1001 なので、close-button の 1005 はさらに上です。
}.close-button の指定はここまでです。
メニューの中央配置
/* メニューの中央配置 */コメントです。
.modal-nav-menu {class="modal-nav-menu" が付いた nav にCSSを当てます。
HTMLだとこれです。
<nav class="modal-nav-menu js-modal-nav-menu" id="navMenu"> height: 100%;高さを親要素いっぱいにします。
親は .menu-modal です。
.menu-modal は全画面なので、nav も画面いっぱいの高さになります。
display: flex;Flexboxを使います。
Flexboxは、要素を中央に置いたり、横並びにしたりするのが得意です。
justify-content: center;横方向の中央に配置します。
align-items: center;縦方向の中央に配置します。
つまり、
display: flex;
justify-content: center;
align-items: center;この3つで、リストを画面のど真ん中に置いています。
}.modal-nav-menu の指定はここまでです。
リストの初期化
/* リストの初期化 */コメントです。
.modal-nav-menu ul {.modal-nav-menu の中にある ul にCSSを当てます。
HTMLだとこれです。
<ul>
<li><a href="#">Products</a></li>
<li><a href="#">About Us</a></li>
<li><a href="#">Dealer Locator</a></li>
</ul> list-style: none;リストの黒丸を消します。
普通の ul には、
・Products
・About Us
・Dealer Locatorのような黒丸が付きます。
それを消しています。
padding: 0;ul の内側の余白を消します。
ブラウザ標準で ul には左側に余白があります。
それをリセットしています。
margin: 0;ul の外側の余白を消します。
text-align: center;中の文字を中央揃えにします。
}.modal-nav-menu ul の指定はここまでです。
リスト項目:最初は下にずらして透明
/* リスト項目:最初は下にずらして透明 */コメントです。
ここから、Products などの各項目にアニメーションを設定します。
.modal-nav-menu li {.modal-nav-menu の中にある li にCSSを当てます。
つまり、
Products
About Us
Dealer Locatorそれぞれの項目です。
opacity: 0;最初は透明にします。
transform: translateY(30px);最初は下に 30px ずらします。
translateY は縦方向の移動です。
translateY(30px) → 下に30px
translateY(0) → 元の位置です。
transition:変化をなめらかにする設定です。
opacity 0.8s ease,透明度の変化を 0.8秒 かけて行います。
transform 0.8s ease;位置の変化を 0.8秒 かけて行います。
つまり、リスト項目は、
透明 → 表示
下に30px → 元の位置を 0.8秒 かけて行います。
margin: 28px 0;上下に 28px の余白を付けます。
左右は 0 です。
上 28px
下 28px
左右 0という意味です。
リスト同士の間隔を空けています。
}.modal-nav-menu li の指定はここまでです。
モーダルが開いたら、リストが上がりながら表示
/* モーダルが開いたら、リストが上がりながら表示 */コメントです。
.menu-modal.is-open .modal-nav-menu li {これは少し長いですが、意味は、
menu-modal に is-open が付いている時、
その中の modal-nav-menu の中の liです。
つまり、メニューが開いた時だけ、リスト項目にこのCSSを当てます。
opacity: 1;透明だったリストを見えるようにします。
transform: translateY(0);下にずれていたリストを元の位置に戻します。
これにより、
下からふわっと上がってくる動きになります。
}この指定はここまでです。
順番に出す
/* 順番に出す */コメントです。
ここから、リストが一つずつ遅れて出る設定です。
.menu-modal.is-open .modal-nav-menu li:nth-child(1) {メニューが開いた時の、1つ目の li にだけCSSを当てます。
1つ目はこれです。
<li><a href="#">Products</a></li> transition-delay: 0.1s;アニメーション開始を 0.1秒 遅らせます。
}1つ目の設定はここまでです。
.menu-modal.is-open .modal-nav-menu li:nth-child(2) {2つ目の li にCSSを当てます。
2つ目はこれです。
<li><a href="#">About Us</a></li> transition-delay: 0.2s;アニメーション開始を 0.2秒 遅らせます。
1つ目より少し遅れて出ます。
}2つ目の設定はここまでです。
.menu-modal.is-open .modal-nav-menu li:nth-child(3) {3つ目の li にCSSを当てます。
3つ目はこれです。
<li><a href="#">Dealer Locator</a></li> transition-delay: 0.3s;アニメーション開始を 0.3秒 遅らせます。
}3つ目の設定はここまでです。
この3つで、
Products 0.1秒後に表示
About Us 0.2秒後に表示
Dealer Locator 0.3秒後に表示になります。
もっと「ふわっ、ふわっ、ふわっ」とゆっくり出したいなら、こうすると良いです。
.menu-modal.is-open .modal-nav-menu li:nth-child(1) {
transition-delay: 0.25s;
}
.menu-modal.is-open .modal-nav-menu li:nth-child(2) {
transition-delay: 0.45s;
}
.menu-modal.is-open .modal-nav-menu li:nth-child(3) {
transition-delay: 0.65s;
}リンク
/* リンク */コメントです。
.modal-nav-menu a {.modal-nav-menu の中にある a タグにCSSを当てます。
つまり、リンク文字です。
<a href="#">Products</a> color: white;文字色を白にします。
text-decoration: none;リンクの下線を消します。
普通のリンクは青色で下線が付きます。
それを消しています。
font-size: 32px;文字サイズを 32px にします。
大きめで、全画面メニューらしい見た目になります。
letter-spacing: 0.08em;文字間を少し広げます。
letter-spacing は文字と文字の間隔です。
Productsを少しゆったり見せる効果があります。
font-weight: 600;文字を少し太くします。
400 が普通くらい、700 が太字くらいです。
600 は少し太めです。
}リンクの指定はここまでです。
ホバー時
/* ホバー時 */コメントです。
hover はマウスを乗せた時の状態です。
.modal-nav-menu a:hover {.modal-nav-menu の中にあるリンクにマウスを乗せた時のCSSです。
opacity: 0.7;少し透明にします。
通常より薄くなるので、
今マウスが乗っていますよと分かります。
}ホバー時の指定はここまでです。
全体の流れ
このCSS全体の動きはこうです。
最初
↓
ハンバーガーボタンだけ右上に表示
メニュー本体は opacity: 0 / visibility: hidden で非表示
ハンバーガーをクリック
↓
JavaScriptで .menu-modal に is-open を付ける
↓
.menu-modal.is-open が効く
↓
全画面オーバーレイがふわっと表示
さらに
↓
.menu-modal.is-open .modal-nav-menu li が効く
↓
リストが下から上がりながら表示
transition-delay により
↓
Products
About Us
Dealer Locator
の順番で表示一番大事な関係はこれです。
.menu-modal {
opacity: 0;
visibility: hidden;
}通常は非表示。
.menu-modal.is-open {
opacity: 1;
visibility: visible;
}is-open が付いたら表示。
そしてJavaScriptで、
menuModal.classList.add("is-open");または、
menuModal.classList.toggle("is-open");をすることで、CSSの状態を切り替えています。
※css display
display: block; は テキストだけでなく画像にも使えます。
というより、display は ほぼすべてのHTML要素に指定できるCSS です。
例えば画像にも使えます。
img {
display: block;
}まず display とは?
display は、その要素を 画面上でどういう箱として扱うか を決めるCSSです。
ざっくり言うと、
この要素を
・横に並ぶ部品として扱うのか
・1行まるごとの箱として扱うのか
・消すのか
・flexの箱にするのかを決めます。
代表的な display
1. display: block;
.box {
display: block;
}これは、1行まるごと使う箱 になります。
代表例はこれです。
<div></div>
<p></p>
<section></section>
<h1></h1>これらは最初からだいたい display: block; です。
イメージはこうです。
要素A ─────────────
要素B ─────────────
要素C ─────────────特徴は、
・基本的に縦に並ぶ
・width / height が効く
・margin / padding が素直に効く
・親の幅いっぱいに広がりやすいです。
2. display: inline;
span {
display: inline;
}これは、文章の中に入る部品 です。
代表例はこれです。
<span></span>
<a></a>
<strong></strong>イメージはこうです。
今日は <span>暑い</span> ですね横に流れていきます。
特徴は、
・文章の一部として横に並ぶ
・width / height が基本的に効きにくい
・上下のmarginが効きにくい
・改行されにくいです。
3. display: inline-block;
.button {
display: inline-block;
}これは、横に並ぶけど、箱としてサイズ指定もできる 便利な指定です。
イメージは、
[ボタンA] [ボタンB] [ボタンC]特徴は、
・横に並ぶ
・width / height が効く
・padding / margin が使いやすいボタン風リンクによく使います。
a {
display: inline-block;
padding: 12px 24px;
}4. display: flex;
.menu {
display: flex;
}これは、中の要素を並べるための指定です。
例えば、
<nav class="menu">
<a>Products</a>
<a>About</a>
<a>Contact</a>
</nav>.menu {
display: flex;
justify-content: center;
align-items: center;
}こうすると、中のリンクを横並びにしたり、中央配置したりできます。
5. display: none;
.menu {
display: none;
}これは、完全に画面から消す 指定です。
特徴は、
・見えない
・場所も取らない
・クリックもできない
・アニメーションしにくいです。
ハンバーガーメニューの初期状態でよく使いますが、ふわっと表示したい場合は opacity や visibility の方が向いています。
画像に display: block; を使えるの?
使えます。
画像 <img> は、初期状態ではだいたい inline系の要素 として扱われます。
<img src="sample.png" alt="" />画像は見た目は四角い箱ですが、ブラウザ的には「文字と同じ行に並ぶ部品」に近い扱いです。
だから、画像の下に少し余白が出ることがあります。
なぜ画像の下に余白ができるの?
画像は初期状態では、文字と同じように ベースライン に揃えられます。
文字には、下に少し余白があります。
例えばアルファベットの、
g
p
yみたいな文字は、下に伸びますよね。
ブラウザは画像も文字と同じ行に置けるようにしているので、その「下に伸びる文字用のスペース」が残ることがあります。
イメージです。
画像 inline の場合
┌───────┐
│ img │
└───────┘
──────── ← 文字のベースライン
↑
ここに少し余白が出ることがあるなので、画像にこれを指定します。
img {
display: block;
}すると、画像は文章の一部ではなく、普通の箱として扱われます。
画像 block の場合
┌───────┐
│ img │
└───────┘
余計なベースライン余白が出にくいテキストでも同じ余白が出るの?
少し違います。
テキストそのものは、もともと文字なので、ベースラインや行の高さがあります。
例えば、
<p>Products</p>には、標準でこういう余白があります。
pタグには標準の margin がある
文字には line-height があるだからテキストの場合に余白が出る理由は、主にこの2つです。
1. margin
2. line-height画像の下の謎の余白は、主に inline要素としてのベースラインの影響です。
テキストの余白を消したい場合
例えば p の余白を消すなら、
p {
margin: 0;
}文字の行間を調整したいなら、
p {
line-height: 1.5;
}または詰めたいなら、
p {
line-height: 1;
}です。
画像の余白を消す方法は他にもある
画像下の余白を消す方法は、display: block; 以外にもあります。
方法1:display block
一番よく使います。
img {
display: block;
}方法2:vertical-align
画像をinlineのまま扱いたい場合はこれもあります。
img {
vertical-align: bottom;
}または、
img {
vertical-align: middle;
}ただ、単独画像やアイコン画像なら display: block; が分かりやすいです。
今回のハンバーガー画像ではなぜ使う?
今回のHTMLはこうですね。
<button id="menuButton">
<img src="images/26971246.png" alt="メニュー" class="hamburger-menu" />
</button>CSSはこうです。
#menuButton {
width: 44px;
height: 44px;
padding: 0;
}
.hamburger-menu {
width: 100%;
height: 100%;
object-fit: contain;
display: block;
}ここで display: block; を入れる理由は、
画像をボタンの中で余計なズレなく扱うためです。
img が inline のままだと、ボタンの中で微妙に下に余白ができたり、中央からズレたりすることがあります。
かなり大事な整理
display: block
箱として扱う
縦に並ぶ
width / height が効くdisplay: inline
文章の一部として扱う
横に並ぶ
width / height が効きにくいdisplay: inline-block
横に並ぶ
でも箱としてサイズ指定できるdisplay: flex
中の要素を並べたり中央配置したりするdisplay: none
完全に消す
場所も取らない今回の一番のポイントはこれです。
.hamburger-menu {
display: block;
}これは、
画像を文字の一部ではなく、普通の箱として扱う
↓
画像下の変な余白やズレを防ぐという目的です。
なので、画像にも display: block; は普通に使います。
むしろ、画像をレイアウト部品として使う時にはかなりよく使います。
