MENU CLOSE

CSSと簡単なJSでできる
ドロワーメニューの実装方法

Drawer Menu
Drawer Menu

はじめに

WEBサイトでよくある実装ですが、スマホ表示の時にいわゆる「ハンバーガーボタン」などを作ってメニューを隠し、タップすると隠していたメニューを大きく開くような「ドロワーメニュー(Drawer Menu)」と呼ばれるメニュー。

スマホサイトでよく使われる手法ですね。今回はその実装方法をメモ。

実装内容

まず実際の画面はこちら。

ドロワーメニューのデモ

一応、スマホでもPCでも動くように作っています。

Githubにソースコードを置いてますのでコードを詳しく見たい方はどうぞ。
https://github.com/inos3910/drawer-menu

HTML

ヘッダーの中にハンバーガーボタンとメニューを用意します。

HTMLの書き方等に特に必須条件はないです。ボタンを置く位置もデザインによって様々なので自由に配置して大丈夫です。今回のデモではヘッダーの中にボタンとメニューを配置するデザインにしたのでこのようなコードになりました。

JS

次にJSを先に書きます。

JSの内容はかなり簡単で、ハンバーガーボタンをタップすると<body>要素に.is-menu-activeというclassを付けたり外したりする、というだけの実装です。

ひとまず完成したJSがこちら。

ES6でモジュール化してWebpackでバンドルするようにしているので4つのファイルに分割しています。

簡単と言う割にはちょっと長いなって感じですね。笑

長くなっている理由は「PCとタブレットやスマホなどのタッチデバイスの切り分け」です。タッチデバイスにはtouchstartイベントを、マウスカーソルを操作するPCデバイスにはclickイベントを使うようにします。

具体的にはtouchEventHandler()という関数を書いて、その中で条件判定しています。

この関数の中で、要素.addEventListenerではなくdocument.addEventListenerにして引数の中でわざわざタップした要素を判定しています。

その理由としては、要素.addEventListenerにしていると、開発時に画面サイズをPCとスマホで頻繁に切り替えていたら時々タップやクリックが反応しなくなる現象が起こるからです。

そもそもPC上でスマホサイズにしたりPCサイズにしたりする人なんて開発者しかいないので無視してもいいかもしれませんし、各画面サイズに合わせた時にリロードしたら治るんですが、個人的に気持ち悪かったのでこういう書き方にしたら治りました。

少し脱線しましたが、このようにタップとクリックを切り分けるとスマホでタップした時の反応速度が若干変わります。

touchイベントは考慮しない場合

もちろんtouchstartは使わずclickイベントだけでも動くので、タップの反応速度よりコードを短くして見通しを良くしたいっていう方は、下記のようにコンパクトにclickイベントだけ書いて動かすことも可能です。

jQueryあり・なしのサンプルですが、やっぱりjQuery楽。笑

自分が実装する時は、よっぽど軽量化が必要なサイト以外はjQuery入れてるので大体これです。実際のところtouchstartイベントとclickでそこまでタップの反応速度が劇的には変わらないので、コードの見通しを優先することが多いです。

経験上、clickの方がバグも少ないです。

CSS

この実装はCSSが1番のポイントです。
JSでクラスの付け替えが実装できれば、あとはCSSが上手に書ければ応用でメニューのデザインは無限に変えられます。

ひとまずデモと同じ横からスライドして出てくるメニューを実装します。

まずは「ドロワーメニュー」の開いた時の状態をSassを使ってコーディングします。CSS設計はflocssを使ってます。

ここでのポイントはz-indexの値です。

レイヤーの重なり順の概念を理解していないとなかなか難しいですが、positionを指定した要素同士で値が大きい方が上に重なります。ここではpositionを指定している「ヘッダー」「ハンバーガーボタン」「メニュー」の中で、ハンバーガーボタンは常に1番上に来るようにして、その下のレイヤーにメニュー、その下にヘッダー、その下にコンテンツ部分、という重なり順になるようにしています。

こうすることで、メニューを表示した時にハンバーガーボタン以外のエリアを全て覆い隠すようなデザインになります。

続いて、閉じた状態でボタンを押すと右側からヌルッと出てくるようにアニメーションさせたいので、transitionを設定してメニューを隠します。加えて、.is-menu-active<body>要素に付いた時にメニューが開くように実装します。

ポイントは親要素の.l-coveroverflow:hidden;を追記して画面外に動かしたメニューを完全に見えないように隠してしまうことです。overflow:hidden;を書かなかったら変な横スクロールが生まれてしまいます。

その他にハンバーガーボタンも同じ要領で、.is-menu-activeが要素に付いた時に×ボタンに変わるように実装しています。

さらに精度を高めるためのCSS

ここまででメニューは動くのですが、もう少し挙動を安定させたい時に指定しておくと良いかもしれないCSSについてメモしておきます。

touch-action: manipulation;

htmlにこのように指定するだけで「ダブルタップでズームする」デフォルトの機能を無効にできます。MDNのCSSリファレンスにはダブルタップの挙動以外に下記のようなことも記載されています。

ダブルタップでズームすることを無効にすることで、ユーザーが画面をタップしたとき、ブラウザーがクリックイベントの生成を待つ必要がなくなります。

https://developer.mozilla.org/ja/docs/Web/CSS/touch-action#manipulation

ということは、タップしたときの反応も若干早くなるかもしれません。
ちなみにデモサイトで指定しているのですが、iOS13.7でSafariで見るとダブルタップが普通に効いてます。意味ないのかも・・・。笑

pointer-events: none;

pointer-events

https://developer.mozilla.org/ja/docs/Web/CSS/pointer-events

このCSSはリンクやボタン、JSでクリックやホバーなどのイベントを登録している要素などのイベントをすべて無効にできます。これを使うとリンクも機能しなくなりますし、ボタンも押せなくなります。

具体的にどこに使うかと言うと、隠れている時のメニューは反応して欲しくないのでまずそこが1つ。

あともう1つが、細かいですが「ハンバーガーボタン」の三本線の<span>要素です。

position:absolute;でボタンの上に載せているので、タップする時に線の方にフォーカスされて反応が遅れたり反応しなかったりする場合があるかもしれないので、念のため書いておいてもいいと思います。

user-select: none;

user-select

https://developer.mozilla.org/ja/docs/Web/CSS/user-select

このCSSは、指定するとスマホの長押しやPCのマウスカーソルで引っ張って選択するアクションができなくなります。

デモサイトではメニュー内のaタグとか、ハンバーガーボタンに指定しています。

このCSSに関しては、使いどころがサイトによってかなり異なります。

例えば今回のようにヘッダー固定してスクロール時に画面に追従するようなものを作っている場合に起こりがちなのですが、ボタンを押した時やスワイプした時にちょっと長押しっぽくなっただけで下に重なった画像が選択されてフォーカスされてしまったり、メニュー内の文字や画像が選択されてメニューを閉じても選択エリアの表示だけが画面上に残ったり、といった不具合ではないけど、操作してる側がうっとうしいなと感じることをクリアにできるので、使いすぎは良くないですが要所要所で使うことをオススメします。

ここまでやればけっこういい感じになると思います!

CSSだけ変えてデザインを変えてみる

最初のデモ以外に2パターン作ってみました。

メニューの表示アニメーションを変えたデモ

メニューのデザインを変えたデモ

正直そんなに変化ないですかね。笑

案件によって表示方法を変えてみたりしますが、どれも結局は最初のデモの応用でclassの付け替えさえできればあとはCSSですべてまかなえます。

全てGithubに上げてますので気になる方はご覧ください。
https://github.com/inos3910/drawer-menu

おわりに

最近のドロワーメニューはほとんどclassの付け替えの手法だけで実装しています。もちろんたまにGSAPのようなアニメーションライブラリを使って、もう少し凝ったものを実装することもあります。

ただ結局アニメーションはよっぽどサイト全体で練られていないと、作る側がいくらこだわってもクライアントにもユーザーにもそんなに響かないかむしろ邪魔になってしまう部分なので、最近はサクッと作れてタップの反応も良いこの手法が1番効率が良い気がしています。

前より便利なCSSが使えるようになってきてJSで制御する部分がかなり減ったこともこの手法が使いやすくなった理由の1つですね。

jQueryなしでもOKなので、ドロワーメニューをさくっと自作したい方にはオススメです!

記事一覧

RELATED

テキストの無限ループアニメーション【CSS + JS】
inoue
inoueのサムネイル
TIPS

【CSS + JS】テキストの無限ループアニメーション

shopify
inoue
inoueのサムネイル
TIPS

【Shopify】カートへの遷移を飛ばしてチェックアウトに進むボタンを設置する

PhotoSwipe v5
inoue
inoueのサムネイル
TIPS

【JS】PhotoSwipe v5を使って画像をポップアップ表示する

AVIF [ gulpでjpg・pngからAVIF画像を生成 ]
inoue
inoueのサムネイル
TIPS

gulpでjpg・pngからAVIF画像を生成する

MutationObserverでDOMを監視[PhotoSwipe(v5)+Swiper(v8)連携]
inoue
inoueのサムネイル
TIPS

【JS】MutationObserverでDOMを監視[PhotoSwipe(v5)+Swiper(v8)連携]

Google Sheets API スプレッドシートのデータをJSONで取得する
inoue
inoueのサムネイル
TIPS

【Google Sheets API】 スプレッドシートのデータをJSONで取得する

NEW POSTS

GA4連携人気記事ランキング機能を自作プラグイン化してみ【WordPress】
inoue
inoueのサムネイル
TRY

【WordPress】GA4連携の人気記事ランキング機能を自作プラグイン化してみた

netlify UPDATE BUILD IMAGE [ビルドイメージの更新]
inoue
inoueのサムネイル
TRY

【netlify】ビルドイメージを更新 [ Ubuntu Xenial 16.04 → Ubuntu Focal 20.04 ]

Google Business Profile API [ PHPでクチコミ一覧取得 ]
inoue
inoueのサムネイル
TIPS

【PHP】Google Business Profile APIを使ってクチコミを取得する

screendot screenshot API
inoue
inoueのサムネイル
TRY

スクリーンショットのAPI「screendot」を使ってみた

window.matchMedia hoverを判定
inoue
inoueのサムネイル
TIPS

【window.matchMedia】メディアクエリでhoverが使えるデバイスを判定

lax.js
inoue
inoueのサムネイル
TIPS

lax.jsの使い方【スクロール連動アニメーションの実装】

ブログ記事一覧
  • HOME
  • TIPS
  • CSSと簡単なJSでできるドロワーメニューの実装方法