javaScriptを使わずにHTMLとCSSだけでドロワーメニューを作ります。ハンバーガーメニューとも言います。
スマホでよく見る3本線のボタンでメニューが出るやつです。
だれでも分かるように、サンプルコードを少しずつ作りながら説明します。
まずは完成したサンプルから。
See the Pen sample drawer menu fix by tadtadya (@tadtadya) on CodePen.
これをHTMLとCSSだけでつくります。
javaScriptを使わないで作る
サンプルをつくっていく前に、JavaScriptを使わないメリットから。
HTMLとCSSだけでつくるとWebサーバにリクエストする回数を最小限にできます。そのしくみは、
- ブラウザからhttpリクエストを送信する。
- Webサーバーから、すべてのコンテンツのHTMLを返す。
- ページを最初に表示するときは、閉じるボタン・メニューなどをかくす。
- コンテンツを表示/非表示する動作をCSSでコントロールする。
これはコンテンツの表示・非表示の切り替えでページ遷移をしません。
また、コンテンツをリクエストで再取得しないとしても、JavaScriptのDOM操作がいらないので、その分だけブラウザの負荷を軽くできます。
あらかじめ、CSSですべての操作のスタイルが定義されているので、CSSOMの再展開がない。
(CSSOMはCSSのDOMみたいなもの。)
(JavaScriptのDOM, CSSOM操作の分がいらない。)
また、HTML, CSSに任せることでJavaScriptのコードを少なくできる。
サンプルコードの内容
今回はステップごとに進めていきます。分かりやすいところで区切っていくので必ず作れます。
サンプルコードでは次のコンテンツを用意します。
- ページ全体のエリア
- メニューの開閉ボタン
- メニューエリア
- ページ全体をおおう半透明シート
ここでウン?と思うのは『半透明シート』ですね?メニューを操作するときはそれ以外を操作をさせないために用意します。
各パーツの機能です。
ページ全体のエリア | 何もしない。 自由にページのコンテンツを配置する。 |
開くボタン | メニューを表示する。 |
閉じるボタン | メニューを閉じる。 |
メニューエリア | メニューのコンテンツを入れるボックス。 ボタンで表示/非表示がコントロールされる。 半透明シートをタップ・クリックすると非表示。 |
半透明シート | メニューが出ているとき ・ページ全体エリアにシートをかぶせる。 ・ページ全体のコンテンツをタップ・クリックさせない。 ・タップ・クリックするとメニューを閉じる。 ・メニューを閉じると消える。 |
ステップ1 - パーツを作る
はじめにそれぞれのパーツを用意します。骨組みはHTMLで肉付けはCSSです。
まだ動きの機能はありません。見た目だけを作ります。
<head>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.0/css/all.css" integrity="sha384-lZN37f5QGtY3VHgisS14W3ExzMWZxybE1SJSEsQp9S+oqd12jhcu+A56Ebc1zFSJ" crossorigin="anonymous">
</head>
<body>
<header>
<div class="menu-drawer">
<input type="checkbox" id="chk" />
<label class="btn" for="chk"></label>
<label class="other" for="chk"></label>
<div class="content">
<h2><i class="fas fa-list-ul"></i>メニュー</h2>
<div class="menu">
<a href="#home"><i class="fas fa-home"></i>home</a>
<a href="#contact"><i class="far fa-envelope"></i>contact</a>
<a href="#phone"><i class="fas fa-phone"></i>phone</a>
<a href="#search"><i class="fas fa-search"></i>search</a>
</div>
<p>© 20xx sample.com</p>
</div>
</div>
</header>
<div class="main-content">
<h2 id="home">home</h2>
<h2 id="contact">contact</h2>
<h2 id="phone">phone</h2>
<h2 id="search">search</h2>
</div>
<footer></footer>
</body>
<head>でアイコンフォントのFont Awesomeを読み込んでいます。ボタンのマークやメニュー項目のアイコンに使います。
使い方はかんたんです。
ヘッダ・フッタ
<header>, <footer>タグはHTML5から追加されました。HTML5からサイトのヘッダ・フッタはこのタグを使います。
サンプルでは<header>にドロワーメニュー(.menu-drawer)を追加しました。
メニューはどのページでも表示する『共通機能』だからです。
ページ全体エリア(.main-content)
サンプルではこのエリアがページ全体を覆います。サイトのコンテンツを追加します。
サンプルではメニューの仮のリンク先があるだけです。
ドロワーメニューのボタン
ドロワーメニューのボタンはチェックボックスとラベルで作りました。この理由はあと回しです。とりあえずこういうもんだと思ってください。
メニューの本体が『.menu-drawer .content』です。サンプルでは少しのリストを追加しました。メニューの中身はすべてこの中に入れます。
あと、メニュー以外をクリック・タップしたときチェックボックスをオフにする半透明シートもラベルで作りました。
これもあとで説明します。
HTMLは以上です。とくにむずかしいことはないでしょう。メニューのボタン、半透明シートが気になるくらいでしょうか。
次のステップからすべてCSSで作業します。
ステップ2 - パーツに装飾をつける
ここまでのHTMLの実行結果を見てみましょう。
home
contact
phone
search
まぁそうなりますね。骨がむき出しの状態です。CSSの最初はパーツに装飾をつけます。
まずはパーツ装飾のすべてのCSSはこちらです。
.menu-drawer .btn {
font-size: 27px;
background: #fff;
width: 40px;
padding: 4px 0;
cursor: pointer;
text-align: center;
top: 0;
right: 0;
}
.menu-drawer .btn::before, .menu-drawer .btn::after {
font-family: "Font Awesome 5 Free";
font-weight: 900;
}
.menu-drawer .btn::before {
content: "\f0c9";
}
.menu-drawer .btn::after {
content: "\f00d";
}
.menu-drawer .other {
background: rgba(0, 0, 0, 0.2);
top: 0;
left: 0;
width: 100%;
height: 100vh;
}
.menu-drawer .content {
display: table;
border: 1px solid #ebebeb;
border-radius: 4px;
background: #fff;
width: 90vw;
top: 34px;
right: 0;
text-align: center;
}
@media screen and (min-width: 415px) {
.menu-drawer .content {
width: 350px;
}
}
.menu-drawer .content h2 {
font-size: 16px;
font-weight: 700;
color: #fff;
background: #383838;
margin: 0;
padding: .7em;
border-radius: 4px 4px 0 0;
}
.menu-drawer .content h2 i {
color: #383838;
background: #fff;
border-radius: 50%;
padding: .4em;
margin-right: .5em;
}
.menu-drawer .content .menu {
height: 210px;
overflow-y: auto;
}
.menu-drawer .content a {
border-bottom: 1px solid #ebebeb;
padding: 1rem 0 0 0;
text-decoration: none;
color: #858585;
display: block;
width: 100%;
display: flex;
flex-flow: column;
}
.menu-drawer .content a i {
font-size: 27px;
}
.menu-drawer .content p {
color: #858585;
}
すぐに分からなければ一気に見る必要はないです。少しずつ分解して説明します。
メニュー開閉ボタン
.menu-drawer .btn {
font-size: 27px;
background: #fff;
width: 40px;
padding: 4px 0;
cursor: pointer;
text-align: center;
top: 0;
right: 0;
}
.menu-drawer .btn::before, .menu-drawer .btn::after {
font-family: "Font Awesome 5 Free";
font-weight: 900;
}
.menu-drawer .btn::before {
content: "\f0c9";
}
.menu-drawer .btn::after {
content: "\f00d";
}
メニュー開閉ボタンは、チェックボックスのラベルを使っているので、そのラベルをボタン風にします。
<input type="checkbox" id="chk" /><label class="btn" for="chk"></label>
.menu-drawer .btn {
font-size: 27px;
background: #fff;
width: 40px;
padding: 4px 0;
cursor: pointer;
text-align: center;
top: 0;
right: 0;
}
そして、ボタンのマークを::before, ::after疑似クラスでつくります。
マークはFont Awesomeのアイコンフォントをつかいます。
::before | 三本線。メニューを開く |
::after | バッテン。メニューを閉じる |
.menu-drawer .btn::before, .menu-drawer .btn::after {
font-family: "Font Awesome 5 Free";
font-weight: 900;
}
.menu-drawer .btn::before {
content: "\f0c9";
}
.menu-drawer .btn::after {
content: "\f00d";
}
ここまでのHTMLを実行してみましょう。
home
contact
phone
search
たいして変わってないですが、これでいいです。いまは開く・閉じるボタンのマークが出ればいいので。
(ボタンをタップ・クリックするとチェックボックスが変化します。)
半透明シート
.menu-drawer .other {
background: rgba(0, 0, 0, 0.2);
top: 0;
left: 0;
width: 100%;
height: 100vh;
}
ページ全体にかぶせる半透明シートです。
ここはたいしたことないです。エリア全体をおおうサイズ指定と、半透明をrgba()をつかってうすい黒にしています。
<input type="checkbox" id="chk" /><label class="other" for="chk"></label>
なんで半透明シートもチェックボックスと連携しているかは、あとで説明します。
メニューコンテンツ
.menu-drawer .content {
display: table;
border: 1px solid #ebebeb;
border-radius: 4px;
background: #fff;
width: 90vw;
top: 34px;
right: 0;
text-align: center;
}
@media screen and (min-width: 415px) {
.menu-drawer .content {
width: 350px;
}
}
.menu-drawer .content h2 {
font-size: 16px;
font-weight: 700;
color: #fff;
background: #383838;
margin: 0;
padding: .7em;
border-radius: 4px 4px 0 0;
}
.menu-drawer .content h2 i {
color: #383838;
background: #fff;
border-radius: 50%;
padding: .4em;
margin-right: .5em;
}
.menu-drawer .content .menu {
height: 210px;
overflow-y: auto;
}
.menu-drawer .content a {
border-bottom: 1px solid #ebebeb;
padding: 1rem 0 0 0;
text-decoration: none;
color: #858585;
display: block;
width: 100%;
display: flex;
flex-flow: column;
}
.menu-drawer .content a i {
font-size: 27px;
}
.menu-drawer .content p {
color: #858585;
}
メニュー本体はただのボックスです。
<div class="content">
<h2><i class="fas fa-list-ul"></i>メニュー</h2>
<div class="menu">
<a href="#home"><i class="fas fa-home"></i>home</a>
<a href="#contact"><i class="far fa-envelope"></i>contact</a>
<a href="#phone"><i class="fas fa-phone"></i>phone</a>
<a href="#search"><i class="fas fa-search"></i>search</a>
</div>
<p>© 20xx sample.com</p>
</div>
.menu-drawer .content {
display: table;
border: 1px solid #ebebeb;
border-radius: 4px;
background: #fff;
width: 90vw;
top: 34px;
right: 0;
text-align: center;
}
@media screen and (min-width: 415px) {
.menu-drawer .content {
width: 350px;
}
}
ポイントは、デバイスのサイズでメニューサイズを変えているところでしょうか?(@media)
ほかは色など基本的なことしかしてません。
.menu-drawer .content h2 {
font-size: 16px;
font-weight: 700;
color: #fff;
background: #383838;
margin: 0;
padding: .7em;
border-radius: 4px 4px 0 0;
}
.menu-drawer .content h2 i {
color: #383838;
background: #fff;
border-radius: 50%;
padding: .4em;
margin-right: .5em;
}
メニューのヘッダです。とくにむずかしいことはしてません。
.menu-drawer .content .menu {
height: 210px;
overflow-y: auto;
}
メニューを配置するエリアです。高さを固定してスクロールをつけました。
.menu-drawer .content a {
border-bottom: 1px solid #ebebeb;
padding: 1rem 0 0 0;
text-decoration: none;
color: #858585;
display: block;
width: 100%;
display: flex;
flex-flow: column;
}
.menu-drawer .content a i {
font-size: 27px;
}
.menu-drawer .content p {
color: #858585;
}
メニューのフォントまわりです。
ポイントは、Flexをつかってメニューを立て並びにしたところです。
ほかはたいしたことありません。
ここまでのHTMLを実行してみましょう。
home
contact
phone
search
おー。やっとメニューらしくなりましたね?
そういえば、top, rightなどメニューの位置を指定したのに変わってません。
そこは、いまは無視してください。
次のステップで、コンテンツのコントロールをします。ここでメニューを完成させます。
ステップ3 - メニューの動きをコントロールする
ここがクライマックスです。
メニューのボタン機能、ボタンを押したときのメニューの動きをつけます。
CSSだけで行ないます。
.menu-drawer .btn,
.menu-drawer .content,
.menu-drawer .other {
position: fixed;
}
.menu-drawer .btn,
.menu-drawer .content {
z-index: 1001;
}
.menu-drawer .other {
z-index: 1000;
}
.menu-drawer #chk {
display: none;
}
.menu-drawer #chk ~ .btn::before {
display: block;
}
.menu-drawer #chk ~ .btn::after {
display: none;
}
.menu-drawer #chk ~ .other {
display: none;
}
.menu-drawer #chk:checked ~ .btn::before {
display: none;
}
.menu-drawer #chk:checked ~ .btn::after {
display: block;
}
.menu-drawer #chk:checked ~ .other {
display: block;
}
.menu-drawer #chk ~ .content {
transform: translate(350%, 0);
transition: transform 0.3s ease-in-out;
}
.menu-drawer #chk:checked ~ .content {
transform: none;
}
メニューのポジション
.menu-drawer .btn,
.menu-drawer .content,
.menu-drawer .other {
position: fixed;
}
.menu-drawer .btn,
.menu-drawer .content {
z-index: 1001;
}
.menu-drawer .other {
z-index: 1000;
}
ステップ2でtop, rigthを定義しているのに位置が変わりませんでした。これを完成させます。
メニューボタン、メニュー本体、半透明シートは、タテにスクロールしても同じ位置で固定にしたいのでfixedをつかいます。
あとは、z-indexでコンテンツの重なりを定義します。
メニュー > 半透明シート > ページコンテンツ
メニューはつねにコントロールできるように一番上。
半透明シートは、メニューを出しているときページコンテンツを操作できないようにメニューとページコンテンツのあいだに重ねます。
ここまでのHTMLを実行してみましょう。
メニューの位置が決まりました。半透明シートも出てますね?
メニューの開閉ボタン、半透明シートをクリック・タップしてみてください。左上のチェックボックスが動くのが分かります。
あともう少しです。このチェックボックスまわりをつくれば完成です。
メニューの表示/非表示
.menu-drawer #chk {
display: none;
}
.menu-drawer #chk ~ .btn::before {
display: block;
}
.menu-drawer #chk ~ .btn::after {
display: none;
}
.menu-drawer #chk ~ .other {
display: none;
}
.menu-drawer #chk:checked ~ .btn::before {
display: none;
}
.menu-drawer #chk:checked ~ .btn::after {
display: block;
}
.menu-drawer #chk:checked ~ .other {
display: block;
}
.menu-drawer #chk ~ .content {
transform: translate(350%, 0);
transition: transform 0.3s ease-in-out;
}
.menu-drawer #chk:checked ~ .content {
transform: none;
}
チェックボックスまわりの処理です。少しずつ分解して説明します。
.menu-drawer #chk {
display: none;
}
.menu-drawer #chk ~ .btn::before {
display: block;
}
.menu-drawer #chk ~ .btn::after {
display: none;
}
.menu-drawer #chk ~ .other {
display: none;
}
まずは初期状態です。
- チェックボックス非表示(none)。
- 開くボタン表示。閉じるボタン非表示。
- 半透明シート非表示。
チェックボックスは機能だけが必要なので見えてはいけません。
最初はメニューをかくしたいので開くボタンだけを表示します。
半透明シートはメニューが出てるときだけ必要なのでかくします。
これはチェックボックスがついてないときの処理です。
.menu-drawer #chk:checked ~ .btn::before {
display: none;
}
.menu-drawer #chk:checked ~ .btn::after {
display: block;
}
.menu-drawer #chk:checked ~ .other {
display: block;
}
ここが最大のポイントです。チェックボックスがついたときの処理です。
こんどは開くボタンをかくして閉じるボタンを表示します。
半透明シートを表示します。
.menu-drawer #chk ~ .content {
transform: translate(350%, 0);
transition: transform 0.3s ease-in-out;
}
.menu-drawer #chk:checked ~ .content {
transform: none;
}
メニュー本体もチェックボックスで動かします。
チェックがついていないときは、ページの右側に追いやってかくしておきます。
チェックがついているときは、かくしたものを元に戻します。
そしてtransitionで、メニューをスーッと出し入れして、なめらかなスライドにします。
ステップ4 - その他を整えて完成
もうこれでメニューはできているのですが、Webページのコンテンツ本体を整えて完成です。
.main-content {
height: 10000px;
background: #cfefff;
color: #383838;
text-align: center;
}
.main-content h2 {
height: 300px;
}
メニューのリンク先が分かりやすいように整形しました。
サンプルでは同一ページでジャンプしていますが、<a>リンクで別ページにジャンプしてもいいです。むしろこちらのほうが多いでしょう。
完成したHTMLを実行してみましょう。
まとめ
HTMLとCSSだけでメニューの動きをコントロールしているのは、チェックボックスと連動しているCSSのchecked疑似クラスです。
ポイントはこれだけといってもいいでしょう。
ほかにも、checked疑似クラスをつかうとHTML・CSSだけでできることがあります。
使い方は同じで、いろいろなことができますよ?