ドロップダウンメニューを作ろう

Javascript

こんにちはkazutoです。今回は、ドロップダウンメニューを作成をしていきましょう。

ドロップダウンメニューとは❓

ドロップダウンメニューとは、選択したメニュー欄を

  • クリックイベント
  • マウスオーバーイベント

というイベントが発火する事で、メニューが垂れ下がるように表示がされていくメニュー欄の事を指します。言葉だけで説明されてもわからないので、下記の画像をご覧ください。

上記は、フリマサイトのメルカリのドロップダウンメニューです。カテゴリーメニューにマウスが乗ったら、カテゴリーが表示される仕組みです。カテゴリー事に表示される項目が変わっているのが画像から分かりますね。

後もう一つほど実例をみて見ましょう。下記の画像を、ご覧ください。

こちらは、僕が自作したドロップダウンメニューになります。階層が深くなるにつれて透明度を上げてます。
今回、こちらのドロップダウンメニューを作成していきます。

以上で、ドロップダウンメニューの説明を終わります。

事前準備

ドロップダウンメニューを実装する前に事前準備を行いましょう。下記のソースコードをコピペしてください。

[イメージ]

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="style.css">
  <script src="index.js"defer></script>
</head>
<body>
  <div class="header">
    <h1 class="header_text">kazuguramming</h1>
      <ul class="lists">
        <li class="list">
          Archive
          <ul class ="menu1">
            <li class="menu1__list">
              プログラミング
              <ul class = "menu1__menu1">
                <li class="menu1__menu1__list active">HTML
                  <ul class ="menu1__menu1__next1">
                    <li class="menu1__menu1__next1__list">body</li>
                    <li class="menu1__menu1__next1__list">html</li>
                    <li class="menu1__menu1__next1__list">head</li>
                  </ul>
                </li>
                <li class="menu1__menu1__list active">CSS
                  <ul class ="menu1__menu1__next2">
                    <li class="menu1__menu1__next2__list">color</li>
                    <li class="menu1__menu1__next2__list">height</li>
                    <li class="menu1__menu1__next2__list">width</li>
                  </ul>
                </li>
                <li class="menu1__menu1__list active">PHP
                  <ul class ="menu1__menu1__next3">
                    <li class="menu1__menu1__next3__list">if文</li>
                    <li class="menu1__menu1__next3__list">条件分岐</li>
                    <li class="menu1__menu1__next3__list">バックエンド</li>
                  </ul>
                </li>
              </ul>
            </li>
            
            <li class="menu1__list">ブログ
              <ul class ="menu1__menu2">
                <li class="menu1__menu2__list active">
                  雑記
                  <ul class ="menu1__menu2__next1">
                    <li class="menu1__menu2__next1__list">趣味</li>
                    <li class="menu1__menu2__next1__list">ゲーム</li>
                    <li class="menu1__menu2__next1__list">ビジネス</li>
                  </ul>
                </li>
                <li class="menu1__menu2__list active">特化
                  <ul class ="menu1__menu2__next2">
                    <li class="menu1__menu2__next2__list">一貫性</li>
                    <li class="menu1__menu2__next2__list">専門性</li>
                    <li class="menu1__menu2__next2__list">権威性</li>
                  </ul>
                </li>
                <li class="menu1__menu2__list active">日記
                  <ul class ="menu1__menu2__next3">
                    <li class="menu1__menu2__next3__list">記録</li>
                    <li class="menu1__menu2__next3__list">自己管理</li>
                    <li class="menu1__menu2__next3__list">楽しい</li>
                  </ul>
                </li>
              </ul>
            </li>
            
            <li class ="menu1__list">筋トレ
              <ul class="menu1__menu3">
                <li class="menu1__menu3__list active">
                  ベンチプレス
                  <ul class ="menu1__menu3__next1">
                    <li class="menu1__menu3__next1__list">50kg</li>
                    <li class="menu1__menu3__next1__list">100kg</li>
                    <li class="menu1__menu3__next1__list">120kg</li>
                  </ul>
                </li>
                <li class="menu1__menu3__list active">
                  懸垂
                  <ul class ="menu1__menu3__next2">
                    <li class="menu1__menu3__next2__list">10回</li>
                    <li class="menu1__menu3__next2__list">20回</li>
                    <li class="menu1__menu3__next2__list">30回</li>
                  </ul>
                </li>
                <li class="menu1__menu3__list active">プッシュアップ
                  <ul class ="menu1__menu3__next3">
                    <li class="menu1__menu3__next3__list">50回</li>
                    <li class="menu1__menu3__next3__list">100回</li>
                    <li class="menu1__menu3__next3__list">120回</li>
                  </ul>
                </li>
              </ul>  
            </li>   
                  
         </ul>
        </li>
        <li class="list">
          skills
          <ul class ="menu2">
            <!-- <li class="menu2__list">HTML</li> -->
            <li class="menu2__list">CSS</li>
            <li class="menu2__list">Ruby</li>
            <li class="menu2__list">php</li>
            <!-- <li class ="menu2__list">Rails</li> -->
            <!-- <li class ="menu2__list">javascript</li> -->
            
          </ul>
        </li>
        <li class="list">
          SNS
          <ul class ="menu3">
            <li class="menu3__list">Twitter</li>
            <li class="menu3__list">Facebook</li>
            <li class ="menu3__list">Instagram</li>
          </ul>           
        </li>
      </ul>
  </div>
 </div>
 




  <div class="main">
    <div class="contents">
      <div class="content1">
       <p class="content1__text">HTML&CSS</p> 
      </div>  
      <div class="content2">
        <p>Ruby</p> 
      </div>
      <div class="content3">
       <p>Ruby on Rails</p> 
      </div>
      <div class="content4">
         <p>javascript</p>   
      </div>
    </div>
  </div>
  
  <div class="footer">
    @kazutotake
  </div>
</body>
</html>

.header{
  background-color:black;
  width: 100%;
  height: 100px;
  color:white ;
  display: flex;
  justify-content: space-around;
  position: fixed;
  top:0;
  left: 0;
}
.lists{
  display: flex; 
  list-style: none;
  /* line-height: 120px; */
  margin-top: 60px;
  /* background-color: brown; */
}
.list{
  /* margin-top: 10px; */
  margin-right: 5px;
  list-style: none;
  font-size: 20px;
  /* background-color: cadetblue; */
  padding: 0 20px;
}

.main{
height: 500px;
width: 100vw;
background-color:rgb(222, 220, 220);
padding-bottom: 100px;
}
.contents{
  padding-top: 100px;
  height: 100%;
  width: 80%;
background-color: white;
 display: flex;
 justify-content: space-around;
 margin: auto;
 font-size: 20px;
}

 .content1{
  margin: auto;
  height: 200px;
  width:200px;
  background-color: cadetblue;
  text-align: center;
  line-height: 150px;
}


.content2{
  margin: auto;
  height: 200px;
  width:200px;
  background-color: red;
  text-align: center;
  line-height: 150px;
}

.content3{
  margin: auto;
  height: 200px;
  width:200px;
  background-color:rgb(135, 71, 71);
  text-align: center;
  line-height: 150px;
}

.content4{
  margin: auto;
  height: 200px;
  width:200px;
  background-color: yellow;
  text-align: center;
  text-align: center;
  line-height: 150px;

} 



.footer{
background-color: black;
height: 100px;
width: 100%;
position: fixed;
bottom: 0;
left: 0;
color: white;
text-align: center;
line-height: 100px;
}



.menu1{
  /* padding-top: 10px; */
  list-style: none;
  position: fixed;
  top: 100px;
  right: 330px;
  opacity: 0;

  /* background-color: chartreuse; */
}
.menu1__list{
  /* padding-top: 10px; */
  color: white;
  font-size: 15px;
  background-color:black;
  border-bottom: 3px solid gray;
  border-right: 3px solid gray;
  padding-right: 5px ;
  padding-left: 10px;
}

 .menu1__menu1{ 
  padding-top: 10px;
  list-style: none;
  position: fixed;
  top: 90px;
  right: 245px; 
  display: none;
 } 

.menu1__menu1__list{
  color: white;
  font-size: 15px;
  background-color:black;
  padding-right:30px ;
  padding-left: 10px;
  border-bottom: 3px solid gray;
  border-right: 3px solid gray;
  border-left:3px solid gray;
}


.menu1__menu1__next1{
  padding-top: 10px;
  list-style: none;
  position: fixed;
  top: 90px;
  right: 148px; 
  display: none;
}

.menu1__menu1__next1__list{
  color: white;
  font-size: 15px;
  background-color:black;
  padding-right:50px ;
  padding-left: 10px;
  border-bottom: 3px solid gray;
  border-right: 3px solid gray;
  border-left:3px solid gray;   
}


.menu1__menu1__next2{
  padding-top: 10px;
  list-style: none;
  position: fixed;
  top: 90px;
  right: 140px; 
  display: none;
}

.menu1__menu1__next2__list{
  color: white;
  font-size: 15px;
  background-color:black;
  padding-right:40px ;
  padding-left: 10px;
  border-bottom: 3px solid gray;
  border-right: 3px solid gray;
  border-left:3px solid gray;   
}

.menu1__menu1__next3{
  padding-top: 10px;
  list-style: none;
  position: fixed;
  top: 90px;
  right: 130px; 
  display: none;
}

.menu1__menu1__next3__list{
  color: white;
  font-size: 15px;
  background-color:black;
  padding-right:10px ;
  padding-left: 10px;
  border-bottom: 3px solid gray;
  border-right: 3px solid gray;
  border-left:3px solid gray;   
}


.menu1__menu2{
  padding-top: 10px;
  list-style: none;
  position: fixed;
  top: 90px;
  right: 230px;
  /* opacity: 0; */
 display: none;
}
.menu1__menu2__list{
  color: white;
  font-size: 15px;
  background-color:black;
  padding-right:50px ;
  padding-left: 20px;
  border-bottom: 3px solid gray;
  border-right: 3px solid gray;
  border-left:3px solid gray;
}



.menu1__menu2__next1{
  padding-top: 10px;
  list-style: none;
  position: fixed;
  top: 90px;
  right: 140px; 
  display: none;
}

.menu1__menu2__next1__list{
  color: white;
  font-size: 15px;
  background-color:black;
  padding-right:20px ;
  padding-left: 10px;
  border-bottom: 3px solid gray;
  border-right: 3px solid gray;
  border-left:3px solid gray;   
}


.menu1__menu2__next2{
  padding-top: 10px;
  list-style: none;
  position: fixed;
  top: 90px;
  right: 140px; 
  display: none;
}

.menu1__menu2__next2__list{
  color: white;
  font-size: 15px;
  background-color:black;
  padding-right:30px ;
  padding-left: 10px;
  border-bottom: 3px solid gray;
  border-right: 3px solid gray;
  border-left:3px solid gray;   
}

.menu1__menu2__next3{
  padding-top: 10px;
  list-style: none;
  position: fixed;
  top: 90px;
  right: 140px; 
  display: none;
}

.menu1__menu2__next3__list{
  color: white;
  font-size: 15px;
  background-color:black;
  padding-right:20px ;
  padding-left: 10px;
  border-bottom: 3px solid gray;
  border-right: 3px solid gray;
  border-left:3px solid gray;   
}



.menu1__menu3{
  display: none;
  /* opacity: 0; */
  padding-top: 10px;
  list-style: none;
  position: fixed;
  top: 90px;
  right: 210px;
}
.menu1__menu3__list{
  color: white;
  font-size: 15px;
  background-color:black;
  padding-right: 5px ;
  padding-left: 10px;
  border-bottom: 3px solid gray;
  border-right: 3px solid gray;
  border-left:3px solid gray;
}



.menu1__menu3__next1{
  padding-top: 10px;
  list-style: none;
  position: fixed;
  top: 90px;
  right: 127px; 
  display: none;
}

.menu1__menu3__next1__list{
  color: white;
  font-size: 15px;
  background-color:black;
  padding-right:20px ;
  padding-left: 10px;
  border-bottom: 3px solid gray;
  border-right: 3px solid gray;
  border-left:3px solid gray;   
}


.menu1__menu3__next2{
  padding-top: 10px;
  list-style: none;
  position: fixed;
  top: 90px;
  right: 130px; 
  display: none;
}

.menu1__menu3__next2__list{
  color: white;
  font-size: 15px;
  background-color:black;
  padding-right:30px ;
  padding-left: 10px;
  border-bottom: 3px solid gray;
  border-right: 3px solid gray;
  border-left:3px solid gray;   
}

.menu1__menu3__next3{
  padding-top: 10px;
  list-style: none;
  position: fixed;
  top: 90px;
  right: 130px; 
  display: none;
}

.menu1__menu3__next3__list{
  color: white;
  font-size: 15px;
  background-color:black;
  padding-right:20px ;
  padding-left: 10px;
  border-bottom: 3px solid gray;
  border-right: 3px solid gray;
  border-left:3px solid gray;   
}




.menu2{
  opacity: 0; 
  padding-top: 10px;
  list-style: none;
  position: fixed;
  top: 90px;
  right: 250px;
  /* background-color: chartreuse; */
}
.menu2__list{
  /* padding-top: 10px; */
  color: white;
  font-size: 15px;
  background-color:black;
  padding-right:50px ;
  padding-left: 10px;
  border-bottom: 3px solid gray;
  border-right: 3px solid gray;
}

.menu3{
opacity: 0; 
 padding-top: 10px;
 list-style: none;
 position: fixed;
 top: 90px;
 right: 150px;
 /* background-color: chartreuse; */
}
.menu3__list{
 /* padding-top: /10px; */
 color: white;
 font-size: 15px;
 background-color:black;
 padding-right:10px ;
 padding-left: 10px;
 border-bottom: 3px solid gray;
 border-right: 3px solid gray;
}

.active{
  opacity: 1;
}

.block{
  display: block;
  /* opacity: 1; */
}
.transparent{
 opacity: 0.4;
}

.ch{
  /* opacity: 1; */
  background-color: black;
  z-index: 3;
}
.zup{
  z-index: 2;
}

.zup3{
  /* z-index: 3; */
  background-color: black;
  /* opacity: 0.8 ; */
}

※少し長いですが必ず全てコピペをしましょう。不備があると動かなくなる恐れがあります。

今回は

  • mouseoverイベント
    →マウスカーソルが乗ったらイベントが発火
  • mouseleaveイベント
    →マウスカーソルが離れたらイベントが発火

を用いてドロップダウンメニューを構築していきます。

ドロップダウンメニュー第1階層

まずは,ドロップダウンメニュー第1階層のメニュー欄を構築していきます。

  • ノードを取得
  • addEventListenerメソッドでイベント登録
  • イベントの発火後の処理を実装

ノードを取得

まずは、ノードを取得をしましょう。

const lists = document.querySelectorAll(".list") 
//階層1
const menu1= document.querySelector(".menu1")
const menu2= document.querySelector(".menu2")
const menu3= document.querySelector(".menu3")
//階層1リスト
const menulists1= document.querySelectorAll(".menu1__list")
const menulists2= document.querySelectorAll(".menu2__list")
const menulists3= document.querySelectorAll(".menu3__list")

// 配列
const menus =[menu1,menu2,menu3]
const menulists = [menulists1,menulists2,menulists3]

今回は、とても定数の量が多いです。なのでデバックをする関数を用意して置くのもありですね。以下の関数を定義しておきましょう。

function forlogs(elments){
 elments.forEach(elment=>{
   console.log(elment);
 })
}

実際に呼び出してデバックをしてみましょう。

確認できたら消してください
forlogs(lists)
forlogs(menulists)
forlogs(menulists1)
forlogs(menulists2)
forlogs(menulists3)

無事、ノードを取得できているのが分かりますね。デバックは、成功です。一つ注意点があります。デバックが終わったら、必ず関数の呼び出す記述を消してください。無駄な記述は、予期せぬエラーを発生させる恐れがあるからです。

document.querySelectorAll(".list") 

新しく,querySelectorAllメソッドが出てきました。
querySelectorAllメソッドとは、初めに合致したノードを取得するquerySelectorメソッドの進化系みたいな物です。合致したノードを全て取得しNodeListというオブジェクトを返り値として返します。

今回は、

  • querySelectorメソッド
  • querySelectorAllメソッド

を用いてかつお互いの特性によって使い分けてノードを取得しています。同じ物だとは認識をせず、仕分けをして実装を進めていきましょう。

addEventListenerメソッドでイベント登録

ノードを取得をできた所でイベントを登録をしていきましょう。まずは、ドロップダウンメニューの最上部の定数listsに

  • mouseoverイベント
  • mouseleaveイベント

を登録をしていきましょう。

定数listsは、querySelectorAllメソッドで複数のノードを取得をしており、その要素ごとにイベントを登録したいので、forEachメソッドを用いて、NodeListからノードを取り出して、イベントを登録をしていきます。

lists.forEach((list,index)=>{
  list.addEventListener("mouseover",function(){
    this.addEventListener("mouseleave",function(){  
    })
  })
})

上記の記述でイベントを登録をできました。処理内容としてmouseoverイベントが発火したらmouseleaveイベントを登録をするという仕様です。またthisは、listを参照しています。thisの挙動はシチュエーションによって変わるので、今は、深く考えずに実装していきましょう。

続いて、イベントが登録をできているのが確認をできていないため、一度、console.log()を用いてデバックをしてみましょう。

lists.forEach((list,index)=>{
  list.addEventListener("mouseover",function(){
   console.log("マウスオーバー")
    this.addEventListener("mouseleave",function(){
   console,log("マウスアウト")
    })
  })
})
  • マウスオーバ
  • マウスアウト

という、文字列が交互に表示されている事が確認できましたので、イベント登録ができた事が分かりました。なので次にステップに進みましょう。続いてイベントの中身を実装していきます。

イベントの発火後の処理を実装

イベント発火後の処理を実装していきましょう。

  • mouseoverイベントが発火したタイミングで、メニュー表示をする
    →addメソッドでクラスを追加
  • mouseleaveイベントが発火したタイミングで、メニューを非表示にする
    →removeメソッドでクラスを削除

それでは実装していきましょう。

function addClass(element,className){
  element.classList.add(className)
}
function remeveClass(element,className){
  element.classList.remove(className)
}

lists.forEach((list,index)=>{
  list.addEventListener("mouseover",function(){
    addClass(menus[index],"active")
    this.addEventListener("mouseleave",function(){
      remeveClass(menus[index],"active")
    })
  })
})
function addClass(element,className){
  element.classList.add(className)
}
function remeveClass(element,className){
  element.classList.remove(className)
}

こちらは、上から順に

  • addClass
    →クラスを追加をする関数
  • remeveClass
    →クラスを削除をする関数

です。

ドロップダウンメニューでは、クラスの追加&削除を繰り返して実装をしていきます。したがって何度もクラス追加&削除の処理を書く必要があります。なので関数化をしました。

lists.forEach((list,index)=>{
  list.addEventListener("mouseover",function(){
    addClass(menus[index],"active")
   addClass(menus[index],"zup")
    this.addEventListener("mouseleave",function(){
     remeveClass(menus[index],"active")
   remeveClass(menus[index],"zup")
    })
  })
})

mouseoverイベントでクラスを追加、mouseleaveイベントでクラスを削除をしています。以上で「ドロップダウンメニュー第1階層」の実装を終わります。

ドロップダウンメニュー第2階層

続いて、ドロップダウンメニュー第2階層を実装していきましょう。

  • ノードを取得
  • addEventListenerメソッドでイベント登録
  • イベントの発火後の処理を実装

ノードを取得

まずは、ノードを取得をしましょう。

//階層2
const nextMenu1= document.querySelector(".menu1__menu1")
const nextMenu2= document.querySelector(".menu1__menu2")
const nextMenu3= document.querySelector(".menu1__menu3")
//階層2リスト
const nextMenu1Lists = document.querySelectorAll(".menu1__menu1__list")
const nextMenu2Lists = document.querySelectorAll(".menu1__menu2__list")
const nextMenu3Lists = document.querySelectorAll(".menu1__menu3__list")
//配列
const nextMenus=[nextMenu1,nextMenu2,nextMenu3]
const nextLists =[nextMenu1Lists,nextMenu2Lists,nextMenu3Lists]

第一階層同様、定数などの量が多いので、ノードが取得ができたかデバックをしていきましょう。

function forlogs(elments){
 elments.forEach(elment=>{
   console.log(elment);
 })
}

forlogs(nextMenu1Lists )
forlogs(nextMenu2Lists )
forlogs(nextMenu3Lists )
forlogs(nextMenus)

無事、コンソールに出力されているので、ノードが取得ができております。続いて,イベントを登録をしていきましょう。

addEventListenerメソッドでイベント登録

続いて、第二階層を

  • 表示
  • 非表示

を行うために第1階層のメニューリストにイベントを登録をしていきます。

登録するイベントは「ドロップダウンメニュー第1階層」と同様

  • mouseoverイベント
  • mouseleaveイベント

を登録していきます。

なお定数 menulistsにイベントを登録をしていきます。

定数 menulistsは、querySelectorAllメソッドで複数のノードを取得をしており、その要素ごとにイベントを登録したいので、forEachメソッドを用いて、NodeListからノードを取り出して、イベントを登録をしていきます。

lists.forEach((list,index)=>{
  list.addEventListener("mouseover",function(){
     addClass(menus[index],"zup")
     addClass(menus[index],"active")
    this.addEventListener("mouseleave",function(){
      remeveClass(menus[index],"active")
      remeveClass(menus[index],"zup")
      menulists.forEach(lists=>{
        lists.forEach((list,index)=>{
          list.addEventListener("mouseover",function(){
            addClass(list,"transparent")
            this.addEventListener("mouseleave",function(){
            })
           }) 
         })
       })
     })
   })
 })

上記の記述でイベントを登録をできました。処理内容としてmouseoverイベントが発火したらmouseleaveイベントを登録をするという仕様です。またthisは、listを参照しています。thisの挙動はシチュエーションによって変わるので、今は、深く考えずに実装していきましょう。

続いて、イベントが登録をできているのが確認をできていないため、一度、console.log()を用いてデバックをしてみましょう。

lists.forEach((list,index)=>{
  list.addEventListener("mouseover",function(){
     addClass(menus[index],"zup")
     addClass(menus[index],"active")
    this.addEventListener("mouseleave",function(){
      remeveClass(menus[index],"active")
      remeveClass(menus[index],"zup")
      menulists.forEach(lists=>{
        lists.forEach((list,index)=>{
          list.addEventListener("mouseover",function(){
            console.log("マウスオーバー");
            addClass(list,"transparent")
            this.addEventListener("mouseleave",function(){
             console.log("マウスアウト")
              remeveClass(list,"transparent")
            })
           }) 
         })
       })
     })
   })
 })
  • マウスオーバ
  • マウスアウト

という、文字列が交互に表示されている事が確認できましたので、イベント登録ができた事が分かりました。なので次にステップに進みましょう。続いてイベントの中身を実装していきます。

イベントの発火後の処理を実装

イベント発火後の処理を実装していきましょう。

  • mouseoverイベントが発火したタイミングで、メニュー表示をする
    →addメソッドでクラスを追加
  • mouseleaveイベントが発火したタイミングで、メニューを非表示にする
    →removeメソッドでクラスを削除

それでは実装していきましょう。

function addClass(element,className){
  element.classList.add(className)
}

function remeveClass(element,className){
  element.classList.remove(className)
}
lists.forEach((list,index)=>{
  list.addEventListener("mouseover",function(){
     addClass(menus[index],"zup")
     addClass(menus[index],"active")
    this.addEventListener("mouseleave",function(){
      remeveClass(menus[index],"active")
      remeveClass(menus[index],"zup")
      menulists.forEach(lists=>{
        lists.forEach((list,index)=>{
          list.addEventListener("mouseover",function(){
            addClass(nextMenus[index],"block")
            addClass(list,"transparent")
            this.addEventListener("mouseleave",function(){
              remeveClass(list,"transparent")
              remeveClass(nextMenus[index],"block")
            })
           }) 
         })
       })
     })
   })
 })

addClassとremeveClassクラスの解説は省略します。

 menulists.forEach(lists=>{
        lists.forEach((list,index)=>{
          list.addEventListener("mouseover",function(){
            addClass(nextMenus[index],"block")
            addClass(list,"transparent")
            this.addEventListener("mouseleave",function(){
              remeveClass(list,"transparent")
              remeveClass(nextMenus[index],"block")
            })
           }) 
         })
       })
addClass(nextMenus[index],"block")
addClass(list,"transparent")
 remeveClass(list,"transparent")
 remeveClass(nextMenus[index],"block")

第二階層のdiv要素にblockというクラスを追加&削除をして

  • 表示
  • 非表示

をしています

第一階層のdiv要素にはlistというクラスを追加&削除をして

  • 透明にする
  • 戻す

をしています。

処理自体は、とてもシンプルなのですが,forEachメソッドなどのループ処理を用いているので難しく感じる事があると思います。なので、ループ処理に慣れていきましょう。以上で、「ドロップダウンメニュー第2階層」の実装を終わります。

ドロップダウンメニュー第3階層

最後に第3階層を実装していきましょう。処理自体は大きく変わる事がないのでもう一踏ん張りがんばっていきましょう。

[手順]

  • ノードを取得
  • addEventListenerメソッドでイベント登録
  • イベントの発火後の処理を実装

ノードを取得

まずは、ノードを取得していきましょう。第3階層なのでとても量が多いですの注意深く実装していきましょう。

//階層3
const tier3Menu1 = document.querySelector(".menu1__menu1__next1")
const tier3Menu2 = document.querySelector(".menu1__menu1__next2")
const tier3Menu3 = document.querySelector(".menu1__menu1__next3")

const tier3Menu11 =document.querySelector(".menu1__menu2__next1")
const tier3Menu22 =document.querySelector(".menu1__menu2__next2")
const tier3Menu33 =document.querySelector(".menu1__menu2__next3")

const tier3Menu111 =document.querySelector(".menu1__menu3__next1")
const tier3Menu222 =document.querySelector(".menu1__menu3__next2")
const tier3Menu333 =document.querySelector(".menu1__menu3__next3")

// 階層3リスト
const tier3Menu1Lists =document.querySelectorAll(".menu1__menu1__next1__list")
const tier3Menu2Lists =document.querySelectorAll(".menu1__menu1__next2__list")
const tier3Menu3Lists =document.querySelectorAll(".menu1__menu1__next3__list")


const tier3Menu11Lists =document.querySelectorAll(".menu1__menu2__next1__list")
const tier3Menu22Lists =document.querySelectorAll(".menu1__menu2__next2__list")
const tier3Menu33Lists =document.querySelectorAll(".menu1__menu2__next3__list")


const tier3Menu111Lists =document.querySelectorAll(".menu1__menu3__next1__list")
const tier3Menu222Lists =document.querySelectorAll(".menu1__menu3__next2__list")
const tier3Menu333Lists =document.querySelectorAll(".menu1__menu3__next3__list")


const tier3Menys1 =[tier3Menu1,tier3Menu2,tier3Menu3]
const tier3Menys11 =[ tier3Menu11,tier3Menu22,tier3Menu33]
const tier3Menys111 =[tier3Menu111,tier3Menu222,tier3Menu333]

const tierLists1 =[ tier3Menu1Lists, tier3Menu2Lists, tier3Menu3Lists]
const tierLists11 = [tier3Menu11Lists, tier3Menu22Lists, tier3Menu33Lists]
const tierLists111 = [tier3Menu111Lists, tier3Menu222Lists, tier3Menu333Lists]

const tierLists =[tierLists1,tierLists11,tierLists111]

最後の最後で、とてもすごいことになってしまいましたね。ドロップダウンメニューの構造上、階層が深くなるほどノードの量が多くなってしまいます。なので注意深くデバックをしていきましょう。
今回は、ノードを上手く配列でまとめられたので、配列の定数のみデバックをします。

forlogs(tier3Menys1)
forlogs(tier3Menys11)
forlogs(tier3Menys111)
forlogs(tierLists1)
forlogs(tierLists11)
forlogs(tierLists111)

無事、コンソールに出力されているので、ノードが取得ができております。続いて,イベントを登録をしていきましょう。

addEventListenerメソッドでイベント登録

続いて、第3階層を

  • 表示
  • 非表示

を行うために第2階層のメニューリストにイベントを登録をしていきます。

登録するイベントは「ドロップダウンメニュー第1階層」と同様

  • mouseoverイベント
  • mouseleaveイベント

を登録していきます。

なお定数 nextListsにイベントを登録をしていきます。

定数 nextListsは、querySelectorAllメソッドで複数のノードを取得をしており、その要素ごとにイベントを登録したいので、forEachメソッドを用いて、NodeListからノードを取り出して、イベントを登録をしていきます。

lists.forEach((list,index)=>{
    list.addEventListener("mouseover",function(){
       addClass(menus[index],"zup")
       addClass(menus[index],"active")
        this.addEventListener("mouseleave",function(){
        remeveClass(menus[index],"active")
        remeveClass(menus[index],"zup")
        menulists.forEach(lists=>{
          lists.forEach((list,index)=>{
            list.addEventListener("mouseover",function(){
              addClass(nextMenus[index],"block")
              addClass(list,"transparent")
             nextLists.forEach(nextlist=>{
              nextlist.forEach((next,index)=>{
                next.addEventListener("mouseover",function(){
                next.addEventListener("mouseleave",function(){
                })
                })
              })
             })
             this.addEventListener("mouseleave",function(){
               remeveClass(list,"transparent")
               remeveClass(nextMenus[index],"block")
             })
            }) 
          })
        })
      })
    })
  })

上記の記述でイベントを登録をできました。処理内容としてmouseoverイベントが発火したらmouseleaveイベントを登録をするという仕様です。

続いて、イベントが登録をできているのが確認をできていないため、一度、console.log()を用いてデバックをしてみましょう。

lists.forEach((list,index)=>{
    list.addEventListener("mouseover",function(){
       addClass(menus[index],"zup")
       addClass(menus[index],"active")
        this.addEventListener("mouseleave",function(){
        remeveClass(menus[index],"active")
        remeveClass(menus[index],"zup")
        menulists.forEach(lists=>{
          lists.forEach((list,index)=>{
            list.addEventListener("mouseover",function(){
              addClass(nextMenus[index],"block")
              addClass(list,"transparent")
             nextLists.forEach(nextlist=>{
              nextlist.forEach((next,index)=>{
                next.addEventListener("mouseover",function(){
                  console.log("マウスオーバー")
                next.addEventListener("mouseleave",function(){
                  console.log("マウスアウト")
                })
                })
              })
             })
             this.addEventListener("mouseleave",function(){
               remeveClass(list,"transparent")
               remeveClass(nextMenus[index],"block")
             })
            }) 
          })
        })
      })
    })
  })

  • マウスオーバ
  • マウスアウト

という、文字列が交互に表示されている事が確認できましたので、イベント登録ができた事が分かりました。なので次にステップに進みましょう。続いてイベントの中身を実装していきます。

イベントの発火後の処理を実装

イベント発火後の処理を実装していきましょう。

  • mouseoverイベントが発火したタイミングで、メニュー表示をする
    →addメソッドでクラスを追加
  • mouseleaveイベントが発火したタイミングで、メニューを非表示にする
    →removeメソッドでクラスを削除

更に今回は、第3階層のノードに透明度を足していきたいです。なのでnextの中で、新たに定数 tierListsの配列に格納されているノードに対して、 mouseover&mouseleaveイベントを追加します。

それでは実装していきましょう。

lists.forEach((list,index)=>{

    list.addEventListener("mouseover",function(){
       addClass(menus[index],"zup")
       addClass(menus[index],"active")
        this.addEventListener("mouseleave",function(){
        remeveClass(menus[index],"active")
        remeveClass(menus[index],"zup")
        menulists.forEach(lists=>{
          lists.forEach((list,index)=>{
            list.addEventListener("mouseover",function(){
              addClass(nextMenus[index],"block")
              addClass(list,"transparent")
             nextLists.forEach(nextlist=>{
              nextlist.forEach((next,index)=>{
                next.addEventListener("mouseover",function(){
                  addClass(next,"transparent")
                  addClass(list,"active")
                  addClass(tier3Menys1[index],"block")
                  addClass(tier3Menys11[index],"block")
                  addClass(tier3Menys111[index],"block")
            tierLists.forEach((tierlist)=>{
                     tierlist.forEach((tier)=>{
                       tier.forEach((ti)=>{
                        ti.addEventListener("mouseover",()=>{
                          addClass(ti,"transparent")
                          ti.addEventListener("mouseleave",function(){
                            remeveClass(ti,"transparent")
                         })
                       })
                     })
                    })
                  })
                next.addEventListener("mouseleave",function(){
                  remeveClass(next,"transparent")
                  remeveClass(tier3Menys1[index],"block")
                  remeveClass(tier3Menys11[index],"block")
                  remeveClass(tier3Menys111[index],"block")
                })
                })
              })
             })
             this.addEventListener("mouseleave",function(){
               remeveClass(list,"transparent")
               remeveClass(nextMenus[index],"block")
             })
            }) 
          })
        })
      })
    })
  })
この画像には alt 属性が指定されておらず、ファイル名は 9af061d693a0c21aadd32a80de755f25.gif です

ソースコードの量が多いので一部、抜粋して解説をしていきます。

 nextLists.forEach(nextlist=>{
              nextlist.forEach((next,index)=>{
                next.addEventListener("mouseover",function(){
                  addClass(next,"transparent")
                  addClass(list,"active")
                  addClass(tier3Menys1[index],"block")
                  addClass(tier3Menys11[index],"block")
                  addClass(tier3Menys111[index],"block")
                next.addEventListener("mouseleave",function(){
                  remeveClass(next,"transparent")
                  remeveClass(tier3Menys1[index],"block")
                  remeveClass(tier3Menys11[index],"block")
                  remeveClass(tier3Menys111[index],"block")
                })
                })
              })
             })

こちらは、第2階層のメニューに対して

  • 透明度を足す&戻す
  • 表示&非表示

を行っております。

  • tier3Menys1
  • tier3Menys11
  • tier3Menys111

の3つにblockクラスを追加&削除を行っている理由は、一つの定数だと管理が難しいためです。第3階層にもなると、項目が増えすぎて一つの定数では、管理するのが難しくなったため、3つの定数に対してクラス追加&削除を行っております。

 tierLists.forEach((tierlist)=>{
    tierlist.forEach((tier)=>{
     tier.forEach((ti)=>{
       ti.addEventListener("mouseover",()=>{
         addClass(ti,"transparent")
          ti.addEventListener("mouseleave",function(){
            remeveClass(ti,"transparent")
                })
              })
            })
          })
        })

こちらは、第3階層のノードに対して

  • mouseoverイベント
  • mouseleaveイベント

を登録しています。

addClass(ti,"transparent")
remeveClass(ti,"transparent")

transparentクラスを追加&削除をする事で、透明度を足す&戻すを再現をしています。

以上で、「ドロップダウンメニュー第3階層」の実装を終わりです。

まとめ:ドロップダウンメニューを作ろう

今回は、ドロップダウンメニューを実装をしていきました。最後にもう一度、 実装に用いたイベントについて振り返りましょう。

  • mouseoverイベント
    →マウスカーソルが乗ったらイベントが発火
  • mouseleaveイベント
    →マウスカーソルが離れたらイベントが発火

定数や配列の数が多く大変でしたね。forEachメソッドなど用いて実装したので、難しく感じる事があるとは思いますが、処理的にはクラスの追加&削除を行っているだけなので、時間があればご自身でドロップダウンメニューを作成をしてみましょう。

以上、kazutoでした。

関連記事

※ドロップダウンメニューを実装にあたりDOMの知識が必要になってきます。今回、DOMの知識が足りないと非常に難易度が上がってしまう可能性があるので以下の記事を見てから、ドロップダウンメニューの実装に入るのを推奨します。