はじめに
Javascript研修を通して、ドロップダウンメニューを実装する。
コーディングルール
- ES6 (EcmaScript2015)以降のモダンな記法を使う
- 関数をモジュール化して使用する
- data 属性を使用してDOM要素を取得する
目指す動作
コード(pug, scss)
すでにpugファイルとscssファイルは用意されており、JSファイルを作成する。
この2つのファイルはすでに用意されているもののため割愛。
.dropdown
.dropdown__inner
nav
ul.dropdown__menu
li.dropdown__menuItem(data-selector="dropdown-menu-item") ABOUT
ul.dropdown__subMenu(data-selector="dropdown-sub-menu")
li.dropdown__subMenuItem about 1
li.dropdown__menuItem(data-selector="dropdown-menu-item") SERVICE
ul.dropdown__subMenu(data-selector="dropdown-sub-menu")
li.dropdown__subMenuItem service 1
li.dropdown__subMenuItem service 2
li.dropdown__menuItem(data-selector="dropdown-menu-item") NEWS
ul.dropdown__subMenu(data-selector="dropdown-sub-menu")
li.dropdown__subMenuItem news 1
li.dropdown__subMenuItem news 2
li.dropdown__subMenuItem news 3
li.dropdown__menuItem(data-selector="dropdown-menu-item") CONTACT
ul.dropdown__subMenu(data-selector="dropdown-sub-menu")
li.dropdown__subMenuItem news 1
li.dropdown__subMenuItem news 2
li.dropdown__subMenuItem news 3
li.dropdown__subMenuItem news 4
@use 'modules/vars';
.dropdown {
display: flex;
justify-content: center;
padding-bottom: 216px;
margin-top: 128px;
&__menu {
display: flex;
background-color: vars.$themeWhite;
}
&__menuItem {
position: relative;
width: 140px;
padding: 16px 0;
font-family: 'League Gothic', sans-serif;
font-size: 24px;
text-align: center;
letter-spacing: 0.1em;
&:hover {
color: vars.$themeWhite;
background-color: vars.$themeBlack;
}
}
&__subMenu {
position: absolute;
display: none;
width: 140px;
margin-top: 16px;
background-color: vars.$themeBlack;
&.--active {
display: block;
}
}
&__subMenuItem {
padding: 16px 0;
font-size: 20px;
letter-spacing: 0.1em;
border: 1px solid vars.$themeBlack;
&:hover {
color: vars.$themeBlack;
background-color: vars.$themeWhite;
}
}
}
JSコード解説
全文は最後に記述しています。
まず1つずつ実際のコードの解説をしていきます。
変数の定義
まずは、ドロップダウンメニューの操作に必要な変数を定義しています。
const dropdownMenuItem = '[data-selector="dropdown-menu-item"]';
const dropdownSubMenu = '[data-selector="dropdown-sub-menu"]';
const ACTIVE = '--active';
ここでは3つの変数を定義しています。
-
dropdownMenuItem
:ドロップダウンメニューの項目を特定するためのセレクタ。 -
dropdownSubMenu
:サブメニューを特定するためのセレクタ。 -
ACTIVE
:サブメニューが表示されている状態を示すCSSクラス名。
上書きされる可能性の低い'ACTIVE'のみ大文字で記述しております。
他の定数はキャメルケースで書いています。
このあたり正解がわからないので、もしアドバイス等あれば教えてください。
イベントハンドラの定義
次に、メニュー項目にマウスが乗ったときや外れたときの動作を定義します。
const showSubMenu = (event) => {
const subMenu = event.target.querySelector(dropdownSubMenu);
subMenu.classList.add(ACTIVE);
};
const hideSubMenu = (event) => {
const subMenu = event.target.querySelector(dropdownSubMenu);
subMenu.classList.remove(ACTIVE);
};
showSubMenu
関数は、メニュー項目にマウスが乗ったときにサブメニューを表示するための関数です。
event.target
event オブジェクトには、イベントが発生した要素情報が含まれています。
event.target で、マウスが乗ったメニュー項目の要素を取得します。
querySelector(dropdownSubMenu)
querySelector
メソッドを使って、マウスが乗ったメニュー項目内のサブメニュー要素を取得します。
classList.add(ACTIVE)
取得したサブメニュー要素に --active
クラスを追加します。
このクラスが付与されることで、CSSで定義されたスタイルによりサブメニューが表示されるようになります。
対照的に、hideSubMenu
関数はサブメニューを非表示にするための関数です。
event.target
と querySelector(dropdownSubMenu)
showSubMenu
と同様に、イベントが発生したメニュー項目要素と、その中のサブメニュー要素を取得します。
classList.remove(ACTIVE)
取得したサブメニュー要素から --active
クラスを削除します。
このクラスが削除されることで、サブメニューが非表示になります。
イベントリスナーの追加
最後に、上記で定義したイベントハンドラをドロップダウンメニューの項目に追加しています。
menuItems.forEach((item) => {
item.addEventListener('mouseenter', showSubMenu);
item.addEventListener('mouseleave', hideSubMenu);
});
ここでは、全てのメニュー項目に対して、マウスが乗ったときは showSubMenu
を実行し、マウスが外れたときは hideSubMenu
を実行するようにしています。
menuItems.forEach((item) => {...})
menuItems
は、ドロップダウンメニューの項目をすべて含むDOM要素のコレクションです。
forEach
は、このNodeList内の各要素に対して、指定された関数を実行するメソッドであり、
すべてのメニュー項目に対して何らかの操作を行うためのものです。(以下動作)
item.addEventListener('mouseenter', showSubMenu)
メニュー項目にマウスが乗ったときに発火するイベントリスナーを追加しています。
mouseenter
は、要素の上にマウスが移動したときに発火するイベントです。
このイベントが発生すると、showSubMenu
関数が実行され、サブメニューが表示されます。
item.addEventListener('mouseleave', hideSubMenu)
この行は、メニュー項目からマウスが離れたときに発火するイベントリスナーを追加しています。
mouseleave
は、要素からマウスが離れたときに発火するイベントです。
このイベントが発生すると、hideSubMenu
関数が実行され、サブメニューが非表示になります。
モジュール化処理を実行し、index.jsでimportする
最終行で下記コマンドを打つことで、モジュール化したJSを外部からimportできるようになり、
それをindex.js でimportしてあげます。
export default dropdown;
import dropdown from './modules/dropdown';
dropdown();
最終的なJSコード
/**
* ドロップダウンメニューを操作する関数
* @function dropdown
*/
const dropdown = () => {
/**
* 'dropdown-menu-item'のdata-selector属性を指定する文字列
* @type {string}
*/
const dropdownMenuItem = '[data-selector="dropdown-menu-item"]';
/**
* 'dropdown-sub-menu'のdata-selector属性を指定する文字列
* @type {string}
*/
const dropdownSubMenu = '[data-selector="dropdown-sub-menu"]';
/**
* サブメニューの表示状態を制御するCSSクラス名
* @type {string}
*/
const ACTIVE = '--active';
/**
* 'dropdown-menu-item'のdata-selector属性を持つすべてのDOM要素を取得
* @type {NodeList}
*/
const menuItems = document.querySelectorAll(dropdownMenuItem);
/**
* 指定したメニュー項目のサブメニューを表示
* @param {Event} event - マウスを置いたときのイベント
*/
const showSubMenu = (event) => {
const subMenu = event.target.querySelector(dropdownSubMenu);
subMenu.classList.add(ACTIVE);
};
/**
* 指定したメニュー項目のサブメニューを非表示にする
* @param {Event} event - マウスを外したときのイベント
*/
const hideSubMenu = (event) => {
const subMenu = event.target.querySelector(dropdownSubMenu);
subMenu.classList.remove(ACTIVE);
};
menuItems.forEach((item) => {
item.addEventListener('mouseenter', showSubMenu);
item.addEventListener('mouseleave', hideSubMenu);
});
};
export default dropdown;
import dropdown from './modules/dropdown';
dropdown();
まとめ
初めてJavaScriptの機能をしっかりと学んで実装したが、JSDocを書いてあげることで型を宣言することができたり、個人的に要素の意味を理解しやすかったりした。
定数に関して、const
は変数と違い上書き不可だと思っていたが、実は絶対ではないとのこと。使い分けがまだ難しい。。慣れるしかない。