使用していないスマートフォンサイトは無いだろうと言えるほど、ポピュラーなドロワーメニュー。drawer.jsなど有名で使いやすいjQueryプラグインも存在しますが、今回はjQueryを使用しないで実装する方法を紹介していきます。
以下のような機能を持ったドロワーメニューの実装をしていきます。

  • 右上にボタンメニューボタンがある
  • ボタンを押したら、右からメニューがスライドインが出てくる
  • メニューの幅は250px
  • メニューオープン時コンテンツエリアはグレーアウト
  • メニューオープン時コンテンツエリアはscroll禁止
  • メニュー内は縦スクロール可能

先に完成したものをご覧ください。

See the Pen drawer-menu by blanks (@blanks-site) on CodePen.


完成したコードを参考に色々説明していきます。


html説明・注意点



  
コンテンツエリア
フッターエリア

今回のhtmlは上記のようになっています。
今回のような場合はナビゲーションのコードは最初や最後の箇所に書けばよいのですが、あえてwrapperの途中にナビゲーションのコードを書いています。
それはレスポンシブなどで使用することを前提にしたコーディングを意識しているからです。

htmlでの注意点は1つです。それは全体を囲むタグをbodyタグ以外で1つ追加することです。今回で言うと、#wrapperになります。

機能の「メニューオープン時コンテンツエリアはscroll禁止」を実現するために、メニューオープン時には全体を囲むタグ(今回は#wrapper)に position:fixed; を指定して、scrollしている分マイナスでtopの座標を指定しています。
この時に全体を囲むタグがbodyタグだとSafariで上手く動かない場合があり、メニューオープン時にページトップへ戻ってしまいます。


css説明・注意点


#wrapper{
 width: 100%;
 overflow-x: hidden;
}
#nav{
  position: fixed;
  z-index: 10;
  right: 0;
  top: 0;
  width: 250px;
  height: 100%;
  transition: .5s transform ease-in-out;
  transform: translateX( 250px );
  -webkit-overflow-scrolling: touch;
}
#nav-inner{
  height: 100%;
  overflow-y: auto;
}
#nav-btn{
  position: fixed;
  right: 10px;
  top: 10px;
  z-index: 11;
  width: 40px;
  height: 40px;
  transition: .5s transform ease-in-out;
  transform: translateX( 0 );
}

//メニューオープン時
.nav-open{
  position: fixed;
  left: 0;
}
.nav-open:after{
  content:'';
  display: block;
  position: fixed;
  z-index: 9;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0,0,0,.6);
}
.nav-open #nav{
  transform: translateX( 0 );
}
.nav-open #nav-btn{
 transform: translateX( -250px );
}

上記はcodepenに記述しているsassの一部で、cssに直したものです。順番に説明していきます。

全体を囲む#wrapperには overflow-x: hidden; が指定しています。これを指定しないとメニュークローズ時に横スクロールが表示されてしまいます。

ナビゲーション全体を囲むnavタグには height: 100%; を指定しなければいけないことを注意してください。指定しないと、メニュー内のスクロールができなくなってしまいます。
-webkit-overflow-scrolling: touch;はiOSで慣性スクロール(滑らかなスクロール)をさせるために指定します。

次に#nav-innerですが、こちらに height: 100%;とoverflow-y: auto;を指定してください。これでメニュー内のスクロールが実現できます。
今回の場合は#nav-innerのdivタグを用意しなくても、#gl-menのulタグに同様の指定をしてもよかったのですが、ナビゲーション全体を囲むnavタグの中にもう1つ全体を囲むタグが必要であると明確に示すために、#nav-innerを用意しています。


次にメニューオープン時のcssに関してです。
メニューオープン時には#wrapperにnav-openクラスが付与するようにしています。

#wrapperは position: fixed; topの値に関してはjsで動的に指定します。

また疑似要素のafter要素でページ全体に不透明度60%の黒背景を敷きます。この時、after要素のz-indexの値を#navや#nav-btnより低い値で1以上を指定します。



javascript説明・注意点

説明は下記コードに書いていますので、割愛させていただきます。

javascriptで書いているので、jQueryライブラリを読み込む必要なく使用できます。


//全体を囲む要素をidで指定
const wrapper = document.getElementById('wrapper'); 

//メニューオープン・クローズのボタン要素をidで指定
const navBtn = document.getElementById('nav-btn'); 

//垂直方向のスクロール位置
var topPotision = 0;

//navBtnクリックしたら navToggle関数実行
navBtn.addEventListener("click", navToggle);


//関数定義

//navBtnクリック時の関数
function navToggle(){
  if(wrapper.classList.contains('nav-open')){
    //メニュークローズ時
    navCloseFunc();
  }else{
    //メニューオープン時
    //オープンした時のスクロール位置を topPotision に格納
    topPotision = navOpenFunc();
  }
}


//メニューオープン時の関数
function navOpenFunc(){
  //スクロール位置を取得
  var getTopPosition = window.pageYOffset;

  //wrapperのstyleに入れる値を wrapperStyle に格納
  var wrapperStyle = "top:-"+getTopPosition+"px;";

  //wrapperのclass「nav-open」を追加
  wrapper.classList.add('nav-open');

  //wrapperにstyle="top:-getTopPosition px;"を入れる
  wrapper.setAttribute('style',wrapperStyle);

  //getTopPositionの値を返す
  return getTopPosition;
}


//メニュークローズ時の関数
function navCloseFunc(){  
  //wrapperのclass「nav-open」を削除
  wrapper.classList.remove('nav-open');

  //wrapperのstyleを削除
  wrapper.removeAttribute("style");

  //topPotisionまで画面を移動させる
  scrollTo(0,topPotision);
}


ドロワーメニューを使うたびに、どうやってしてたっけ?って調べていたので、まとめることができてよかったです。
スクロール禁止で e.preventDefault(); を使う場合もあるようですが、メニュー内のスクロールまでできなくなってしまって、上手く使うことでできませんでした。(指定した要素だけスクロール解除するって方法もあったけど、それも上手く動かなったし。。)

もっとスッキリとメニュー内のスクロールができるドロワーメニューの実装方法があれば教えてほしいです。