【Rails&jQuery】多段階ナビゲーションバー実装を細かく説明してみた。

#使ったもの
Ruby on Rrails 5.2.3
haml 5.1.1
SCSS 3.7.4
jQuery 1.12.4

(すでにMVC等の設定は済ませているものとします）

###作ったきっかけ
あるスクールでメルカリのクローンサイトをチーム開発で作っています。そのメルカリのヘッダーに「多段階ナビゲーションバー」があり、結構つまづきました。

↓これ！
<img width="997" alt="スクリーンショット 2019-07-11 21.30.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434778/5af164dd-d1f7-b2cd-7477-03f3eebdeadc.png">

作ってみると意外とシンプルなもので、慣れれば３０分もしないでカンタンに作れそうです！

#実際にミニアプリで再現してみた!

メルカリで実装できたので、復習用でミニアプリでも似た多段階ナビゲーションバーを作ってみました。
今回この記事で作っていくもののサンプルはこれです。
![7e4cff2a8d2e46edaafae7534ec8113c.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434778/124ca95a-59da-5cf6-3bff-cd82faba32cf.gif)


##hamlでリストを作る

```index.html.haml
%header
  .header
    %ul.parent-wrap
      %li.parent
        %h3
          =link_to '/' do
            %spna リスト１
        %ul.child-wrap
          %li.child
            =link_to '/' do
              %span 1-1
            %ul.grand-child-wrap
              %li.grand-child
                =link_to '/' do
                  %span 1-1-1
              %li.grand-child
                =link_to '/' do
                  %span 1-1-2
              %li.grand-child
                =link_to '/' do
                  %span 1-1-3
          %li.child
            =link_to '/' do
              %span 1-2
            
          %li.child
            =link_to '/' do
              %span 1-3
            
      %li.parent
        %h3
          =link_to '/' do
            %span リスト２
        %ul.child-wrap
          %li.child
            =link_to '/' do
              %span 2-1
            %ul.grand-child-wrap
              %li.grand-child
                =link_to '/' do
                  %span 2-1-1
              %li.grand-child
                =link_to '/' do
                  %span 2-1-2
              %li.grand-child
                =link_to '/' do
                  %span 2-1-3
          %li.child
            =link_to '/' do
              %span 2-2
          %li.child
            =link_to '/' do
              %span 2-3

      %li.parent
        %h3
          =link_to '/' do
            %span リスト３
        %ul.child-wrap
          %li.child
            =link_to '/' do
              %span 3-1
          %li.child
            =link_to '/' do
              %span 3-2
          %li.child
            =link_to '/' do
              %span 3-3

      %li.parent
        %h3
          =link_to '/' do
            %spna リスト4
        %ul.child-wrap
          %li.child
            =link_to '/' do
              %span 4-1
          %li.child
            =link_to '/' do
              %span 4-2
          %li.child
            =link_to '/' do
              %span 4-3

```

このようにかくと
<img width="856" alt="スクリーンショット 2019-07-11 21.41.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434778/71937d6a-0344-2803-b95c-ecc7a2efaaab.png">

ul,liで書いている順番通りに、縦一直線に並んでいきます。
（本来ならばrenderやeach文で短縮します。）

こうみると長いコードで同じことの繰り返しなので、混乱すると思います。しかし、実際はかなりシンプルな構造になっています。
<img width="848" alt="スクリーンショット 2019-07-11 21.44.41.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434778/d96cd4ed-a428-8938-7f5e-5a541b5092c2.png">


<font color='yellow'>黄色枠 = 「孫」要素</font>
<font color='red'>赤色枠 = 「子」要素</font>
<font color='blue'>青色枠 = 「親」要素</font>

ulが親要素だったり子、孫要素の大きなハコ。
liが親、子や孫の１つ１つのハコになっています。

##SCSSで見た目を整える

SCSSでは、hamlで作ったハコを整えていきます。確認のためにdisplay:block;にしておきます。

```index.scss

//active,active2はjQueryで使います
.active{
  display: block;
}
.active2{
  display: block;
}

header{
  width:100vw;
  height: 100vh;
  a{
    color:black;
    text-decoration: none;
  }
}
.header{
  width:80%;
  height: 100px;
  margin: 0 auto;
  background-color:grey;
  display: flex;
}

//親カテゴリー
.parent-wrap{
  display: flex;
  width: 100%;
  height: 100px;
  .parent{
    width: 25%;
    height: 100px;
    h3{
      line-height: 100px;
      text-align: center;
      &:hover{
        background-color:yellow;
      }
    }
  }
}

//子カテゴリー
.child-wrap{
  display: block; //のちにdisplay:none；にします
  background-color:white;
  width:auto;
  position: absolute;
  .child{
    width:279px;
    height: 70px;
    line-height: 70px;
    &:hover{
      background-color: red;
    }
    span{
      font-size: 14px;
      padding:8px 16px;
      text-decoration: none;
      color:black;
    }
  }
}

//孫カテゴリー
.grand-child-wrap{
  display: block;　//のちにdisplay:none；にします
  width:auto;
  position: absolute;
  top:0;
  left:279px;
  bottom:0;
  background: #fff;

  .grand-child{
    width:279px;
    height: 70px;
    line-height: 70px;
    &:hover{
      background-color: red;
    }
    
  }
}
```

だいぶ長いコードですがこちらも@mixinと@includeを使えばかなり短くできます。
すると、下の画像のようになると思います。

<img width="1153" alt="スクリーンショット 2019-07-11 22.35.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434778/9c022eca-beab-e50e-e5ec-36c63da0d54b.png">

（hoverしてみると色がつきます)

###縦にul,li、横にabsolute

hamlだけだと縦一直線でした。それをscssで横に並べるべきものを横に並べました。
それを実現しているのは、

```index.scss
.child{
    width:279px;
}

.grand-child-wrap{
  position: absolute;
  top:0;
  left:279px;
  bottom:0;
}

```

このコードです。**position:absolute;で位置を固定し、top/left/bottomで位置を調整しています。leftが279と微妙なのは、その上にある.childのwidthの幅を合わせているためですね。**

##jQueryで動きをつける

ここからはjQueryで動きをつけていきます。ここがかなり難しいと踏んでいたんですが、hoverしたときの動きを書いていけばいいだけなので、コード自体もかなりシンプルです。

<br>

```header.js
$(document).ready(function(){

  $('.parent').hover(function(){
    $(this).addClass('active');
    var children = $('.active').children('.child-wrap');
    children.show();
  },function(){
    $(this).removeClass('active');
    $(this).children('.child-wrap').hide();
  });

  $('.child').hover(function(){
    $(this).addClass('active2');
    var grandChild = $('.active2').children('.grand-child-wrap');
    grandChild.show();
  },function(){
    $(this).removeClass('active2');
    $(this).children('.grand-child-wrap').hide();

  });
});
```
jQueryをRailsで使えるようにするためには、「[jQueryをrailsで使えるようにする](https://qiita.com/ngron/items/95846bd630a723e00038)」の記事を参考にしました。
<br>
.parentをhoverしたとき、つまり親のli要素をhoverしたときに発火させます。さらにaddClassを使い、activeクラスをつけてあげることで、display:block;がつくようになります。（css参照)
<br>
ここではvar childrenとしているのですが、変数をつかわなくても大丈夫です。
**.activeをつけたクラスの子要素を取得(childrenメソッド)しています。.child-wrapとは、子要素の大きなハコ、つまりul要素です。**

hoverの話を少しすると、hoverの第一引数はhoverしている時のアクションで、第二引数はhoverが外れた時のアクションです。なので、第二引数部分にはremoveClassとhideメソッドを使っています。

```index.js
$('.parent').hover(function(){
//第一引数。ここにaddClass,show
},function(){
//第二引数　ここにremoveClass.hide
});
```

###忘れないdisplay:none;

jQueryで動きをつけたら、cssでdisplay:block;にしていた部分をdisplay:none;にします。

すると、サンプル通り動きがつくと思います。

#あとがき
だいぶ長くなってしまいました。記事内でなんども言ってはいますが、本来は部分テンプレートや@mixin等で共通部分の書き出しをするので、もっと短くなります。
<br>
実装前は「なにこれ、難しそう。てかhtmlどうなってんの？？？」という感じでしたが、一個ずつ細かく分解して見ていくことで、多段階ナビゲーションバーの構成が理解できました。だいぶ時間かかったけど・・・
<br>
もっと深いナビゲーションバーにしたい！という場合でも孫要素からは同じことの繰り返しなので、他にも応用できそうですね。

誤字脱字、記述ミスなどありましたら、よろしくお願いします。
#参考サイト
[JavaScript逆引きリファレンス](https://javascript.programmer-reference.com/jquery-list-method/)
[jQueryで多階層のナビゲーションメニューを実装する](https://designsupply-web.com/knowledgeside/2922/)
[【jQuery入門】 addClass()でクラス属性を操作する方法まとめ！](https://www.sejuku.net/blog/47314)