🙌

【レスポンシブ・アコーディオン付き】Flexboxを駆使したナビゲーションメニューを作成

2022/08/28に公開

こんにちは。PeiWebです。
前回の記事に引き続き、Flexboxの復習がてら、ECサイトで使うナビゲーションメニューを実装したので共有します。

完成図

PC

PC版PC版(1500px程度)

タブレット

タブレットタブレット版(750px程度)

スマホ

スマホスマホ版(400px程度)
※黒い枠線は関係なし

また、「絞り込み検索」をクリックすると、
アコーディオン実装アコーディオン実装結果
※黒い枠線は関係なし
上記のような画面になるようにしました。

次に、コードを見ていきます。

HTML

index3.html
<!DOCTYPE html>
<html lang="ja">
<head>
 <meta charset="UTF-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>PeiWeb-フレックスボックス</title>
 <link rel="stylesheet" href="css/style3.css">
</head>
<body>
<header class="site-header">
 <div class="site-header__wrapper">
   <div class="wrapper">
     <div class="header-left">
       <div class="header-facet js-facet">
         <p class="header-facet__text">絞り込み検索</p>
       </div>
       <nav class="nav">
         <ul class="nav__wrapper">
           <li class="nav__item"><strong>在庫</strong> 在庫あり 在庫なし</li>
           <li class="nav__item"><strong>サイズ</strong> S M L</li>
           <li class="nav__item"><strong>金額</strong> 0円 <input type="range" name="speed" min="0" max="30000"
               step="1000">30,000円 </li>
         </ul>
       </nav>
     </div>
     <div class="header-right">
       <div class="header-button">
         並び替え↓
       </div>
     </div>
   </div>
 </div>
</header>
<!-- Header End -->
<script src="js/main3.js"></script>
</body>

</html>

大きいグループで言うと、「絞り込み検索」のクラスとnavタグと「並び替え」のクラスでグループ分けしているようなイメージですかね。。

CSS

style3.css
body{
  margin: 0;
  padding: 0;
}
.site-header{
  background-color: #def7ff;
}
@media screen and (max-width: 600px) {
.site-header {
  border: 1px solid #000;
  background-color: #fff;
  height: 80px;
  }
}
.site-header__wrapper {
  margin: 0 auto;
  width: 95%;
}
@media screen and (max-width: 600px) {
  .site-header__wrapper {
    width: 100%;
  }
}
.wrapper{
  display: flex;
  justify-content: flex-end;
  gap: 30px;
  align-items: center;
}
@media screen and (max-width: 600px) {
  .wrapper {
    justify-content: center;
    gap: 0;
    height: 80px;
  }
}

.header-left{
 display: flex;
 align-items: flex-start;
}

@media screen and (max-width: 600px) {
  .header-left {
    display: block;
    border-right: 1px solid #000;
    width: 50%;
    height: 80px;

  }
}

@media screen and (max-width: 600px) {
  .nav {
    line-height: 0;
    height: 0;
    overflow: hidden;
    opacity: 0;
    transition-duration: 0.4s;
    
  }
}
.nav.is-open {
  -moz-box-sizing: border-box;
  box-sizing: border-box;
  border: 1px solid black;
  width: 100vw;
  line-height: normal;
  height: auto;
  opacity: 1;
}
.nav__wrapper {
    display: flex;
    flex-wrap: wrap;
    gap: 20px;
    list-style-type: none;
}
@media screen and (max-width: 600px) {
  .nav__wrapper {
    flex-direction: column;
  }
}
.header-facet{
  position: relative;
}
@media screen and (max-width: 885px) {
  .header-facet {
    margin: 0;
    width: 180px;
    text-align: center;
  }
}
@media screen and (max-width: 600px) {
  .header-facet {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100%;
    height: 80px;
    font-size: 20px;
  }
}

.header-facet::after{
  content: "";
  position: absolute;
  top: 0;
  right: -8px;
  display: inline-block;
  margin: 16px 0;
  width: 1px;
  height: 20px;
  background-color: #030303;
}
@media screen and (max-width: 885px) {
  .header-facet::after {
    right: 0;
  }
}
@media screen and (max-width: 600px) {
  .header-facet::after {
  background-color: #fff;
    }
  .header-facet__text::after {
  content: "↓";
  position: absolute;
  top: 24px;
  display: inline-block;
  }
}

.js-facet.is-active {
  background-color: #def7ff;
}

@media screen and (max-width: 600px) {
  .header-right {
    width: 50%;
    height: 80px;
  }
}
  .header-button {
    padding: 5px;
    background-color: #eee;
    border: 1px solid #000;
  }

@media screen and (max-width: 885px) {
  .header-button {
    width: 100px;
    text-align: center;
  }
}
@media screen and (max-width: 600px) {
  .header-button {
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 0;
    border: none;
    width: 100%;
    height: 80px;
    font-size: 20px;
    background-color: #fff;
  }
}

CSSは、flexboxを多用しております。
詳しくは前回の記事で、Flexboxについて記載しているので、そちらをご覧ください。

ご質問ありましたら、TwitterのDMからご質問ください。

JavaScript

main3.js
      const menu = document.querySelectorAll(".js-facet");
 
      function toggle() {
        const content = this.nextElementSibling;
        this.classList.toggle("is-active");
        content.classList.toggle("is-open");
      }
      for (let i = 0; i < menu.length; i++) {
        menu[i].addEventListener("click", toggle);
      }
      

詳しく見ていきます。

querySelectorAll()

指定したセレクターの条件と一致したHTML要素を全て返すメソッドです。
そのため、

 const menu = document.querySelectorAll(".js-facet");

クリックする要素であるjs-facetというクラスを取得して、menuという変数に代入しております。

toggle関数

対象となるHTML要素を簡単に表示・非表示することができる関数です。
今回は、スマホ版で「絞り込み検索」をクリックするごとに、「表示」「非表示」を繰り返してほしいので、toggle関数にしております。中身を見ていきます。

nextElementSibling

「nextElementSibling」は次の要素を取得するプロパティです。
ここでは、

  const content = this.nextElementSibling;

上記のように書き、「nextElementSibling」でthis(直前の要素である'「js-facet」')の隣の要素である'「nav」'を取得しております。(下記のコード参照)
この'navクラス'をcontentという変数に代入しています。

<!-- 「js-facet」の要素 -->
<div class="header-facet js-facet">
  <p class="header-facet__text">絞り込み検索</p>
</div>
<!-- 「js-facet」の隣の要素 -->
<nav class="nav">
  <ul class="nav__wrapper">
    <li class="nav__item"><strong>在庫</strong> 在庫あり 在庫なし</li>
    <li class="nav__item"><strong>サイズ</strong> S M L</li>
    <li class="nav__item"><strong>金額</strong> 0円 <input type="range" name="speed" min="0" max="30000"
	step="1000">30,000円 </li>
  </ul>
</nav>

classList

特定の要素のクラス名を追加したり、削除したり、参照したりすることが出来る便利なプロパティです。classListにはいくつかメソッドがあるため、下記に記載します。

メソッド 説明
add 該当する要素にクラスを追加
remove 該当する要素にクラスを削除
contains 該当する要素にクラスがあるか確認
toggle 該当する要素にクラスがあれば削除、なければ追加する

ここでは、toggleメソッドを用いて以下のように記述しております。

this.classList.toggle("is-active");
content.classList.toggle("is-open");

つまり、
this(js-facet)のクラスに、is-activeクラスを追加・削除
content(nav)のクラスに、is-openクラスを追加・削除
を行っております。

addEventListener

マウスによるクリックやキーボードからの入力といった様々なイベント処理を実行するメソッドです。

// 書き方
対象の要素.addEventListener(イベント名, 関数, false(省略可));

第1引数にイベントの種類、第2引数に関数、第3引数にイベント伝搬の方式をtrueかfalseで指定、第3引数は通常はfalseを指定します。

イベント名を一部紹介します。

イベント名 説明
click マウスボタンをクリックしたとき
scroll 画面がスクロールしたとき
mousedown マウスボタンを押しているとき
mouseup マウスボタンを離したとき
mousemove マウスカーソルが移動したとき
keydown キーボードのキーを押したとき
keyup キーボードのキーを離したとき
keypress キーボードのキーを押しているとき

今回は、下記のようにfor文を使って要素にクリックイベントをつくりました。

for (let i = 0; i < menu.length; i++) {
menu[i].addEventListener("click", toggle);
}

これで、「絞り込み検索」を押したときにナビゲーションメニューが表示されるようになりました!

こんな感じで実装完了ですね^^


最後に、、、
少しでも参考になれば記事にイイねしていただけますと嬉しいです。

Webサイト & ECサイト構築のご相談・ご依頼はTwitterのDMまでお願いいたします!
お待ちしております^^
https://twitter.com/pei_traveler


Discussion