一つ開くと他が閉じる状態のアコーディオンメニュー(ドロップダウンメニュー)をJavaScriptで実装してみる(パターン3)(備忘録)

今回は、一つのアコーディオンメニューを展開すると、他のアコーディオンメニューが閉じた状態になるアコーディオンメニュー(ドロップダウンメニュー)を実装してみる。なんかすごくくどい文章になっているけど気にせず続けていく。

それでは解説に入っていく。

htmlの記述について

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>アコーディオンメニュー</title>
  <link rel="stylesheet" href="../reset.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main>
    <section>
      <div class="ly_inner" id="menu1">
        <h2>menu1</h2>
        <!-- アコーディオン -->
        <div class="bl_accordion">
          <h3 class="bl_accordionTitle">タイトル1</h3>
          <div class="bl_accordionContent">
            <p>タイトル1の内容が入りますタイトル1の内容が入りますタイトル1の内容が入りますタイトル1の内容が入りますタイトル1の内容が入ります</p>
            <p>タイトル1の内容が入りますタイトル1の内容が入りますタイトル1の内容が入りますタイトル1の内容が入りますタイトル1の内容が入ります</p>
            <p>タイトル1の内容が入りますタイトル1の内容が入りますタイトル1の内容が入りますタイトル1の内容が入りますタイトル1の内容が入ります</p>
            <p>タイトル1の内容が入りますタイトル1の内容が入りますタイトル1の内容が入りますタイトル1の内容が入りますタイトル1の内容が入ります</p>
          </div>
          <h3 class="bl_accordionTitle">タイトル2</h3>
          <div class="bl_accordionContent">
            <p>タイトル2の内容が入ります</p>
            <p><img src="img_sample01.jpg" alt="サンプル画像"></p>
          </div>
          <h3 class="bl_accordionTitle">タイトル3</h3>
          <div class="bl_accordionContent">
            <p>タイトル3の内容が入ります</p>
            <ul class="md_markList">
              <li>タイトル3の内容が入ります</li>
              <li>タイトル3の内容が入りますタイトル3の内容が入りますタイトル3の内容が入りますタイトル3の内容が入ります</li>
              <li>タイトル3の内容が入ります</li>
              <li>タイトル3の内容が入ります</li>
            </ul>
          </div>
        </div>
      </div>
    </section>
  </main>
  <script src="style.js"></script>
</body>
</html>

ソースは下記の記事の時と変わっていない。

該当の記事はこちら

cssの記述について

@charset "utf-8";

/* ==========================
  初期設定
========================== */
*,
*::before,
*::after {
  box-sizing: border-box;
}
img {
  width: 100%;
}

/* ==========================
  アコーディオンメニュー
========================== */
.bl_accordion {
  background-color: #fff;
}
.bl_accordionTitle {
  font-size: 28px;
  font-weight: bold;
  background-color: #d6a194;
  padding: 20px 90px 20px 40px;
  transition: 0.3s;
  cursor: pointer;
  position: relative;
}
.bl_accordionTitle::before {
  content: "";
  width: 35px;
  height: 35px;
  border-right: 4px solid #000;
  border-bottom: 4px solid #000;
  position: absolute;
  top: 10px;
  transform: rotate(45deg);
  right: 40px;
  transition: 0.3s;
}

.bl_accordionTitle.active::before {
  transform: rotate(225deg);
  top: 30px;
}

.bl_accordionTitle:hover::before,
.bl_accordionTitle.active::before {
  border-right: 4px solid #fff;
  border-bottom: 4px solid #fff;
}
.bl_accordionTitle:hover,
.bl_accordionTitle.active {
  background-color: #6d2412;
  color: #fff;
}
.bl_accordionContent {
  padding: 0 40px;
  opacity: 0;
  height: 0;
  line-height: 0;
  transition: 0.3s;
}
.bl_accordionContent > * + * {
  margin-top: 0;
}
.bl_accordionContent img {
  height: 0;
}
.bl_accordionTitle:not(:first-of-type) {
  border-top: 1px solid #6d2412;
}

.bl_accordionContent.open {
  padding: 40px;
  opacity: 1;
  line-height: normal;
  height: auto;
}
.bl_accordionContent.open > * + * {
  margin-top: 10px;
}
.bl_accordionContent.open img {
  height: auto;
}
/* ==========================
  タブ以外のコンテンツの中身
========================== */
.ly_inner {
  width: 100%;
  max-width: 1080px;
  margin: 100px auto;
  padding: 30px;
  background-color: #ccc;
}
.ly_inner h2 {
  font-size: 250%;
  font-weight: bold;
  margin-bottom: 30px;
}
.md_textblock > * + * {
  margin-top: 10px;
}
.md_markList {
  list-style-type: disc;
  margin-left: 1.4rem;
}

/* ====================================
  ここからPC幅
==================================== */
@media screen and (min-width: 768px) {
  .hp_displaySP {
    display: none !important;
  }
}

/* ====================================
  ここからスマホ幅
==================================== */
@media screen and (max-width: 767px) {
  .hp_displayPC {
    display: none !important;
  }
}

こちらも内容は下記の記事と変わっていない。

該当の記事はこちら

JavaScriptの記述について

'use strict';

// アコーディオン
document.addEventListener('DOMContentLoaded', function(){

  const accordionTitle = document.querySelectorAll('.bl_accordionTitle');

  for (let i = 0; i < accordionTitle.length; i++) {
    accordionTitle[i].addEventListener("click", function () {
      for (let j = 0; j < accordionTitle.length; j++) {
        if (accordionTitle[i] !== accordionTitle[j]) {
          accordionTitle[j].classList.remove("active");
          accordionTitle[j].nextElementSibling.classList.remove('open');
        }
      }
      this.classList.toggle('active');
      this.nextElementSibling.classList.toggle('open');
    });
  }
});

今回は以前のタブ切り替えの記事で実装したソースを少し改修して対応してみた。

やりたいこととしては、accordionTitleをクリックしたら、クリックしたものは展開し、それ以外の展開しているものは閉じるような処理をしたい。

クリックしたものはtoggleでいけそうだ。しかし、他のアコーディオンを閉じるというのは、クリックしたもの以外のクラスが外れるようにリセットする必要がある。順を追ってみていきたいと思う。

まず、accordionTitleに対して、for文(i)で繰り返し処理を行う。これでaccordionTitleをクリックする時に何番目をクリックしているのかが認識できるようになる。

さらに、for文(i)の入れ子構造にして、accordionTitleをクリックしたときに、for文(j)で繰り返し処理をする。これにより、iの番号情報とjの番号情報が同じになる。

ということは、iの番号情報とjの番号情報が一致しない時だけ、他の要素のクラスを外すように設定するとうまくいきそうだと考えられるのではないだろうか。

完全に一致しない場合は、「!==」を使えば良いので、条件を設定する。後の記述は前回解説しているので省略したいと思う。

これでJavaScriptのアコーディオンパターンは以上だ。次はjQueryを使った対応について書いていきたいと思う。